Spring Boot - 在集成测试中通过 "new" 关键字创建的模拟有状态对象
SpringBoot - Mock stateful object created via "new" keyword in integration test
我有一个 SpringBoot 应用程序,它由一个 Controller 层和一个 Service 层组成。
MyController
可以通过 @Autowired
访问 MyService
,而 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
执行期间更改其状态,直到我得到所需的结果,因此我不应该自动装配它也不应该将它注入构造函数,因为那样会使用这个 [=41 的单个实例=] 每次调用 myMethod
。我知道存在“原型”bean,但据我所知,即使我将 MyClass
声明为原型 bean 并通过 @Autowired
将其注入 MyService
,该服务仍会使用执行期间 MyClass
的相同实例,所以最终我决定只使用 new
.
最近我一直在尝试进行集成测试,调用我的 Controller 层,它又会调用我的 Service 层,后者又会创建 MyClass
的实例。问题是 MyClass
的许多方法之一在内部调用外部服务,这不应该是测试本身的一部分,所以我想模拟这个 class.
我知道模拟是通过依赖注入完成的,但在这种情况下我不能那样做。是否有替代方法来模拟 MyClass
,或者这种设置根本不可能?如果没有,那么我如何重构我的代码以在这种特殊情况下进行模拟?
非常感谢。
我会回答我自己的问题。
由于 MyClass
保持状态,它不应自动连接到服务,也不应通过其构造函数注入,而是应根据需要创建新实例。然而,whan can 是一个创建这些实例的“工厂”:
@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
是一样的;无论哪种方式,我都会得到一个新实例。好处来自测试;现在我可以模拟工厂 returns,因为工厂是 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.
}
}
可能不是最优雅的解决方案,但至少解决了我当前的问题。
我有一个 SpringBoot 应用程序,它由一个 Controller 层和一个 Service 层组成。
MyController
可以通过 @Autowired
访问 MyService
,而 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
执行期间更改其状态,直到我得到所需的结果,因此我不应该自动装配它也不应该将它注入构造函数,因为那样会使用这个 [=41 的单个实例=] 每次调用 myMethod
。我知道存在“原型”bean,但据我所知,即使我将 MyClass
声明为原型 bean 并通过 @Autowired
将其注入 MyService
,该服务仍会使用执行期间 MyClass
的相同实例,所以最终我决定只使用 new
.
最近我一直在尝试进行集成测试,调用我的 Controller 层,它又会调用我的 Service 层,后者又会创建 MyClass
的实例。问题是 MyClass
的许多方法之一在内部调用外部服务,这不应该是测试本身的一部分,所以我想模拟这个 class.
我知道模拟是通过依赖注入完成的,但在这种情况下我不能那样做。是否有替代方法来模拟 MyClass
,或者这种设置根本不可能?如果没有,那么我如何重构我的代码以在这种特殊情况下进行模拟?
非常感谢。
我会回答我自己的问题。
由于 MyClass
保持状态,它不应自动连接到服务,也不应通过其构造函数注入,而是应根据需要创建新实例。然而,whan can 是一个创建这些实例的“工厂”:
@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
是一样的;无论哪种方式,我都会得到一个新实例。好处来自测试;现在我可以模拟工厂 returns,因为工厂是 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.
}
}
可能不是最优雅的解决方案,但至少解决了我当前的问题。