首页 > 解决方案 > SpringBoot - 在集成测试中通过“new”关键字创建的模拟状态对象

问题描述

我有一个由控制器层和服务层组成的 SpringBoot 应用程序。

MyController可以访问MyServicevia @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 并将其注入MyServicevia @Autowired,该服务在执行期间仍会使用相同的实例MyClass,所以最终我决定只使用new.

最近我一直在尝试做一个集成测试,调用我的 Controller 层,它又会调用我的 Service 层,它又会创建一个MyClass. 问题是MyClass内部调用外部服务的众多方法之一,它不应该是测试本身的一部分,所以我想模拟这个类。

我知道模拟是通过依赖注入完成的,但在这种情况下我不能这样做。是否有替代方法来模拟MyClass,或者这种设置根本不可能?如果不是,那么在这种特殊情况下,我如何重构我的代码以使模拟成为可能?

提前谢谢了。

标签: javaspring-bootjunitmockingintegration-testing

解决方案


我会回答我自己的问题。

由于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.

    }

}

可能不是最优雅的解决方案,但至少解决了我当前的问题。


推荐阅读