java - SpringBoot - 在集成测试中通过“new”关键字创建的模拟状态对象
问题描述
我有一个由控制器层和服务层组成的 SpringBoot 应用程序。
MyController
可以访问MyService
via @Autowired
,同时MyService
有一个创建 的新实例的方法,该实例MyClass
是从外部依赖项导入的。
import externaldependency.MyClass;
@Service
public class MyService {
public void myMethod() {
MyClass c = new MyClass();
c.doStuff();
c.doOtherStuff();
c.doMoreStuff();
}
}
我new
用来创建实例是因为MyClass
持有状态;它有几种方法可以在执行期间更改其状态,myMethod
直到我得到所需的结果,因此我不应该自动装配它,也不应该将它注入构造函数中,因为每次调用都会使用此类的单个实例myMethod
。我知道存在“原型”bean,但据我所知,即使我声明MyClass
为原型 bean 并将其注入MyService
via @Autowired
,该服务在执行期间仍会使用相同的实例MyClass
,所以最终我决定只使用new
.
最近我一直在尝试做一个集成测试,调用我的 Controller 层,它又会调用我的 Service 层,它又会创建一个MyClass
. 问题是MyClass
内部调用外部服务的众多方法之一,它不应该是测试本身的一部分,所以我想模拟这个类。
我知道模拟是通过依赖注入完成的,但在这种情况下我不能这样做。是否有替代方法来模拟MyClass
,或者这种设置根本不可能?如果不是,那么在这种特殊情况下,我如何重构我的代码以使模拟成为可能?
提前谢谢了。
解决方案
我会回答我自己的问题。
由于MyClass
保持状态,它不应该自动连接到服务,也不应该通过其构造函数注入,而是应该根据需要创建新实例。但是,可以自动装配的 whan 是创建这些实例的“工厂”:
@Component
class MyClassFactory {
public MyClass getInstance() {
return new MyClass();
}
}
因此,服务变为:
@Service
public class MyService {
@Autowired
private MyClassFactory myClassFactory;
public void myMethod() {
// MyClass c = new MyClass();
MyClass c = myClassFactory.getInstance();
c.doStuff();
c.doOtherStuff();
c.doMoreStuff();
}
}
new
在实践中,使用工厂和使用;是一样的。无论哪种方式,我都会得到一个新实例。好处来自于测试;现在我可以模拟工厂返回的内容,因为工厂是 Spring 应用程序上下文的一部分:
@SpringBootTest
public class MyTest {
@MockBean
private MyClass myClassMock;
@MockBean
private MyClassFactory myClassFactoryMock;
@Test
public void myTests() {
// Make a mock of MyClass, replacing the return
// values of its methods as needed.
given(
myClassMock.doStuff()
).willReturn(
"Something useful for testing"
);
// Then make a mock of the factory, so that it returns
// the mock of the class instead of a real instance.
given(
myClassFactoryMock.getInstance()
).willReturn(
myClassMock
);
// Do the tests as normal.
}
}
可能不是最优雅的解决方案,但至少解决了我当前的问题。