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

huangapple go评论82阅读模式
英文:

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,否则不要使用。同时不要将@InjectMocksMockito.mock()@Mock一起使用。

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

System.out.println()在您的代码中不起作用,因为您为对象bizdao创建了模拟对象。当您使用模拟对象调用时,实际方法(即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.

huangapple
  • 本文由 发表于 2020年4月11日 06:45:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/61149740.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定