我读了很多关于春天内心嘲笑的问题和答案,但我不能把我的场景付诸实践…
我需要对一个Spring服务bean (a)进行单元测试,该bean (a)@Autowire
另一个服务bean (B),并在其中执行一个方法B.method()。这个方法在bean (A)的@PostConstruct
方法中执行。我使用Spring Boot 2.5 (JUnit 5)。
我@Autowire
bean (A)在我的测试类,但我找不到一种方法来模拟B.method(),所以当我运行@Autowire
(A),它自动连接(B),当A执行B.method(),该方法被模拟。
我试过@Spy
,@SpyBean
,@MockBean
,…
class TestClass {
@Autowired
private A a;
@Test
mytest () {
a.anyMethod();
}
}
@Service
class A {
@Autowired B b;
@Postconstruct
public void postconstruct() {
b.methodToBeMocked();
}
}
@Service
class B {
public void methodTobeMocked(){
}
}
是棘手的可能指出,应用程序的设计可能得到改善。
@MockBean
替换测试执行的现有bean。前面调用了@PostConstruct
。
您可以使用@TestConfiguration
向您的上下文中提供已经模拟和启动的bean。
我添加了一个简单的、可执行的例子:
package de.trion.training;
@SpringBootTest(classes = {MockingSample.class, MockingSample.B.class,
MockingSample.A.class, MockingSample.MockInit.class})
public class MockingSample
{
@Autowired
private A a;
//too late!
//@MockBean
//private static B b;
//@BeforeEach
//void setUp()
//{
// when(b.methodToBeMocked()).thenReturn("mocked!");
//}
@TestConfiguration
static class MockInit
{
@Primary
@Bean
B makeB()
{
var b = mock(B.class);
when(b.methodToBeMocked()).thenReturn("mocked!");
return b;
}
}
@Test
void mytest()
{
a.anyMethod();
}
@Service
public static class A
{
@Autowired
private B b;
private String result;
@PostConstruct
public void postconstruct()
{
result = b.methodToBeMocked();
System.out.println("postconstruct: " + result);
}
public void anyMethod()
{
System.out.println("anymethod: " + result);
}
}
@Service
public static class B
{
public String methodToBeMocked()
{
return "real";
}
}
}
试着让你的单元测试尽可能简单,不需要启动Spring来测试它。你正在测试A的代码,但没有测试Spring是否正确配置和初始化A。
这意味着您可以简单地通过构造函数手动将B注入A,并在测试任何A的方法之前手动调用@Postconstruct
方法。
也不需要@SpringBootTest
。只是一个普通的JUnit5测试5朋友,像下面应该够了。它也会使你的测试运行得更快,因为它不需要引导Spring。
@ExtendWith(MockitoExtension.class)
public class ATest {
@Mock
private B b;
private A a;
@BeforeEach
public void init(){
a = new A(b);
}
@Test
public void fooTest(){
//stub B method
when(b.methodToBeMocked()).thenReturn("xxxxxxxx");
//manually call @Postconstruct before starting testing on A
a.postconstruct();
//start your testing on A
a.someMethod();
}
}