在 JUnit/Mockito 测试 returns 零中注入 bean

Injecting beans in a JUnit/Mockito test returns zero

我有一个带有无状态 EJB 的 JavaEE 应用程序,用于业务逻辑 (EjbBusiness) 和数据库访问 (EjbDAO)。我需要 运行 对 EjbBusiness 进行单元测试,但 DAO 方法总是 return 为零。

在下面的示例中,我同时进行了 类 和单元测试。我模拟连接到数据库的 EjbDAO 方法,return 测试 SQL 连接:

@Stateless
public class EjbDAO {


    public Connection getConnFromPool() {
        Connection conn = null; // in production this would return a connection
        return conn;
    }


    public int add2(int i) {
        Connection conn = getConnFromPool();
        System.out.println("in EjbDAO: " + i);
        return i + 2;
    }

}


@Stateless
public class EjbBusiness {


    @Inject
    private EjbDAO dao;


    public int add2(int i) {
         int j = dao.add2(i);
         System.out.println("in EjbBusiness: " + j);
         return j;

    }

}

因为我模拟了EjbDAO的一个方法,所以我在UnitTest:

中用@Spy注解了它
@RunWith(MockitoJUnitRunner.class)
public class UnitTest {

    @InjectMocks
    private EjbBusiness biz;

    @InjectMocks
    @Spy
    private EjbDAO dao;

    @Before
    public void setup() {
        dao = Mockito.mock(EjbDAO.class);
        biz = Mockito.mock(EjbBusiness.class);
        MockitoAnnotations.initMocks(this);
    }


    @Test
    public void testBean() {

        // this would return the testing connection
        Mockito.doReturn(null).when(dao).getConnFromPool();

        int i = biz.add2(3);

        assertThat(5).isEqualTo(i);
    }
}

问题是断言不起作用,因为 biz.add2(3) return 是零而不是 5。此外,两个 bean 中的 System.out.println 都没有打印出来。如何 declare/mock 用于测试的 bean 起作用?

仅在调用实际方法时使用@InjectMocks,否则不要使用它。也不要一起使用 @InjectMocksMockito.mock() or @Mock

在您的代码中,您在 dao 对象上使用 @InjectMocks,并且您还为此创建了 mock。当您想存根方法调用而不是调用实际方法时,请使用 Mockito.mock()

System.out.println() 在您的代码中不起作用,因为您为对象 bizdao 创建了模拟。当您使用模拟对象调用时,实际方法(即 add2() 因此您得到 0 作为输出)未执行。

有关何时使用 @InjectMocks 的更多信息,请参阅 this

@RunWith(MockitoJUnitRunner.class)
public class UnitTest {

    @InjectMocks
    private EjbBusiness biz;

    @Mock
    private EjbDAO dao;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }


    @Test
    public void testBean() {

        // this would return the testing connection
        Mockito.doReturn(null).when(dao).getConnFromPool();
        Mockito.doCallRealMethod().when(dao).add2(Mockito.anyInt());

        int i = biz.add2(3);

        assertThat(i).isEqualTo(5);
    }
}

您不应使用一个单元测试来测试两个 class。 你应该有两个测试 classes 来测试它们。

例如,

@RunWith(MockitoJUnitRunner.class)
public class EjbBusinessTest {

    @InjectMocks
    private EjbBusiness biz;

    @Mock
    private EjbDAO dao;


    @Test
    public void testAdd2() {

        // this would return the testing connection
        Mockito.doReturn(null).when(dao).getConnFromPool();
        Mockito.doReturn(5).when(dao).add2();

        int i = biz.add2(3);

        assertThat(5).isEqualTo(i);
    }
}

在上面的 class 中,我们只测试方法 EjbBusinessTest.add2 并且我们不关心会发生什么或者方法 EjbDAO.add2 是否正常工作。在这方面,我们应该关心的是被测方法是否正常工作,因此我们模拟该方法外部的所有内容。

EjbDAO.add2 也采用类似的方法,测试用例应如下所示。我还将方法 EjbDAO.getConnection 设为私有,这样它也应该包含在测试中。如果您需要将其设为私有或 public,则应由您做出此选择。如果你决定保留它 public 那么你应该在 EjbDAO 上使用 @Spy 并模拟 EjbDAO.getConnection 方法。


@RunWith(MockitoJUnitRunner.class)
public class EjbDAOTest {

    //instantiate this object the way you want. Mock the external objects used inside this like the library used to get connection inside EjbDAO.getConnection() Method  
    @InjectMocks
    private EjbDAO dao;


    @Test
    public void testAdd2() {

        // I would suggest you to make the getConnection method private.
        // do not mock the getConnection here, instead mock how you are getting the connection inside the  getConnection method. 
        int i = dao.add2(3);

        assertThat(5).isEqualTo(i);
    }
}

希望对您有所帮助。