英文:
Injecting beans in a JUnit/Mockito test returns zero
问题
以下是翻译好的内容:
我有一个使用无状态 EJB(Stateless EJBs)的 JavaEE 应用程序,用于业务逻辑(EjbBusiness
)和数据库访问(EjbDAO
)。我需要在 EjbBusiness
上运行单元测试,但是 DAO 方法始终返回零。
在下面的示例中,我有这两个类和单元测试。我模拟了连接到数据库的 EjbDAO
方法,以返回一个测试用的 SQL 连接:
@Stateless
public class EjbDAO {
public Connection getConnFromPool() {
Connection conn = null; // 在生产环境中,这将返回一个连接
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() {
// 这将返回一个测试连接
Mockito.doReturn(null).when(dao).getConnFromPool();
int i = biz.add2(3);
assertThat(5).isEqualTo(i);
}
}
问题是断言不起作用,因为 biz.add2(3)
返回零而不是 5。另外,两个 bean 中的 System.out.println
未被打印。如何声明/模拟 bean 以使测试正常工作?
英文:
I have a JavaEE application with Stateless EJBs that I use for business logic (EjbBusiness
) and database access (EjbDAO
). I need to run a unit test on EjbBusiness
, but the DAO method always returns zero.
In the example below I have both classes and the unit test. I mock the EjbDAO
method that connects to the database, to return a testing SQL connection:
@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;
}
}
Since I mock one of the methods of EjbDAO, I annotate it with @Spy in UnitTest
:
@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);
}
}
Problem is that the assertion doesn't work, as biz.add2(3)
returns zero instead of 5. Also, the System.out.println
in both beans is not printed. How to declare/mock the beans for the test to work?
答案1
得分: 1
只有在调用实际方法时才使用@InjectMocks
,否则不要使用。同时不要将@InjectMocks
与Mockito.mock()
或@Mock
一起使用。
在您的代码中,您在dao
对象上使用了@InjectMocks
,同时还为其创建了模拟对象。当您想要存根方法调用而不是调用实际方法时,请使用Mockito.mock()
。
System.out.println()
在您的代码中不起作用,因为您为对象biz
和dao
创建了模拟对象。当您使用模拟对象调用时,实际方法(即add2()
,因此您得到0
作为输出)不会被执行。
有关何时使用@InjectMocks
的更多信息,请参见此链接。
@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);
}
}
英文:
Use @InjectMocks
only when you calling actual method otherwise don't use it. And also don't use @InjectMocks
and Mockito.mock() or @Mock
together.
In your code you are using @InjectMocks
on dao
object and you are also creatign mock for that. And use Mockito.mock()
when you want to stub the method calls instead of calling actual methods.
System.out.println()
is not working in your code because you created mocks for objects biz
and dao
. Actual methods (i.e add2()
because of this you are getting 0
as output) not executed when you call with mock objects.
For more info on when to use @InjectMocks
refer
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);
}
}
答案2
得分: 1
你不应该使用一个单元测试来测试两个类。你应该有两个测试类来分别测试它们。
例如,
@RunWith(MockitoJUnitRunner.class)
public class EjbBusinessTest {
@InjectMocks
private EjbBusiness biz;
@Mock
private EjbDAO dao;
@Test
public void testAdd2() {
// 这将返回测试连接
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
方法设为私有,以便将其包含在测试中。是否将其设为私有还需您决定。如果您决定将其设为公共方法,则应在 EjbDAO
上使用 @Spy,并模拟 EjbDAO.getConnection
方法。
@RunWith(MockitoJUnitRunner.class)
public class EjbDAOTest {
// 根据您的需要来实例化这个对象。在此内部使用的外部对象(例如用于在 EjbDAO.getConnection() 方法中获取连接的库)需要进行模拟
@InjectMocks
private EjbDAO dao;
@Test
public void testAdd2() {
// 我建议您将 getConnection 方法设置为私有。
// 不要在这里模拟 getConnection,而是模拟您在 getConnection 方法中获取连接的方式。
int i = dao.add2(3);
assertThat(5).isEqualTo(i);
}
}
希望对您有所帮助。
英文:
You should not use one unit test to test both classes.
You should have two test classes to test them.
For Example,
@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);
}
}
In the above class we are testing only the method EjbBusinessTest.add2
and we don't care what happens or if the method EjbDAO.add2
is working properly. In this all we should care is whether the method under test is working properly, hence we mock everything external to that method.
Following a similar approach for EjbDAO.add2
as well, The test case should look like something given below. I have also made the method EjbDAO.getConnection
private so that that should also be included in the test. This choice should be made by you if you need to make it private or public. If you decide to keep it public then you should use @Spy on EjbDAO
and mock the EjbDAO.getConnection
method.
@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);
}
}
Hope it helps.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论