Java 在JUnit/Mockito测试中注入bean返回零

Java 在JUnit/Mockito测试中注入bean返回零,java,junit,mockito,ejb,Java,Junit,Mockito,Ejb,我有一个带有无状态EJB的JavaEE应用程序,用于业务逻辑(EjbBusiness)和数据库访问(EjbDAO)。我需要在EjbBusiness上运行一个单元测试,但是DAO方法总是返回零 在下面的例子中,我有两个类和单元测试。我模拟连接到数据库的EjbDAO方法,以返回测试SQL连接: @Stateless public class EjbDAO { public Connection getConnFromPool() { Connection conn = n

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

在下面的例子中,我有两个类和单元测试。我模拟连接到数据库的
EjbDAO
方法,以返回测试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)
返回零而不是5。另外,两个bean中的
System.out.println
也不会打印。如何声明/模拟bean以使测试工作?

仅在调用实际方法时使用
@InjectMocks
,否则不要使用它。也不要同时使用
@InjectMocks
Mockito.mock()或@mock

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

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

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

@RunWith(MockitoJUnitRunner.class)
公共类单元测试{
@注射模拟
私营电子商务;
@嘲弄
私家EjbDAO道;
@以前
公共作废设置(){
initMocks(this);
}
@试验
公共void testBean(){
//这将返回测试连接
Mockito.doReturn(null).when(dao.getConnFromPool();
Mockito.doCallRealMethod().when(dao.add2)(Mockito.anyInt());
int i=业务地址2(3);
资产(i)isEqualTo(5);
}
}

仅当调用实际方法时才使用
@InjectMocks
,否则不要使用它。也不要同时使用
@InjectMocks
Mockito.mock()或@mock

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

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

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

@RunWith(MockitoJUnitRunner.class)
公共类单元测试{
@注射模拟
私营电子商务;
@嘲弄
私家EjbDAO道;
@以前
公共作废设置(){
initMocks(this);
}
@试验
公共void testBean(){
//这将返回测试连接
Mockito.doReturn(null).when(dao.getConnFromPool();
Mockito.doCallRealMethod().when(dao.add2)(Mockito.anyInt());
int i=业务地址2(3);
资产(i)isEqualTo(5);
}
}

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

比如说,

@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);
    }
}
在上面的类中,我们只测试方法
EjbBusinessTest.add2
,我们不关心会发生什么,也不关心方法
EjbDAO.add2
是否正常工作。在这方面,我们应该关心的是被测试的方法是否正常工作,因此我们模拟该方法外部的一切

同样,对于
EjbDAO.add2
也采用类似的方法,测试用例应该如下所示。我还将方法
EjbDAO.getConnection
设置为私有,因此该方法也应包含在测试中。如果您需要将其设置为私有或公共,则应由您进行选择。如果您决定将其公开,那么应该使用@Spy on
EjbDAO
并模拟
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);
    }
}

希望有帮助。

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

比如说,

@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);
    }
}
在上面的类中,我们只测试方法
EjbBusinessTest.add2
,我们不关心会发生什么,也不关心方法
EjbDAO.add2
是否正常工作。在这方面,我们应该关心的是被测试的方法是否正常工作,因此我们模拟该方法外部的一切

同样,对于
EjbDAO.add2
也采用类似的方法,测试用例应该如下所示。我还将方法
EjbDAO.getConnection
设置为私有,因此该方法也应包含在测试中。如果您需要将其设置为私有或公共,则应由您进行选择。如果您决定将其公开,那么应该使用@Spy on
EjbDAO
并模拟
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);
    }
}

希望能有所帮助。

这是可行的,但是这种方法的问题是,如果dao对象中只有一个方法需要模拟,并且有10个方法需要运行real方法,那么我需要为每个real方法声明
Mockito.doCallRealMethod()
。有没有相反的方法?例如,要声明整个类和只需模拟一个方法。@ps0604您能否给出一个示例这是可行的,但是这种方法的问题是,如果dao对象中只有一个要模拟的方法,并且有10个方法需要运行实方法,那么我需要为每个实方法声明
Mockito.doCallRealMethod()
。有没有相反的方法?例如,声明整个类和