如何测试服务或正确模拟? Java11, Spock 框架

How to test a service or do it right mock? Java11, Spock Framework

各位同仁,欢迎大家!告诉我如何决定,或如何行动。 (Java11, SpringBoot, testing - Spock Framework) 我需要编写一个测试 class 方法的测试,整个问题是被测 class 的方法调用了另一个服务通过继承,它不是在被测 class 中声明的,而是在其抽象祖先中声明的。如何测试这样的故事?如果这个服务是在测试本身的 class 中声明的,那么一切都很清楚,我会在测试中创建一个模拟并将它传递给构造函数,但是如果这个服务位于祖先怎么办?我在下面附上示例代码。

// The class to be tested

@Service
public class ServiceForTest extends AbstractComponent{

    public String methodForTest (String s) {
        return someService.generateString(s);
    }

}

//An abstract class from which the tested one is inherited and which contains the service

public class AbstractComponent {

    @Autowired
    protected SomeService someService;

}

public interface SomeService {

    String generateString(String s);

}

@Service
public class SomeServiceImpl implements SomeService{

    @Override
    public String generateString(String s) {
        return s;
    }

}

下面是一个示例,说明如果服务在 class 正在测试中我会做什么

//TestClass
@Service
public class ServiceForTest extends AbstractComponent{

    final SomeService someService;

    public ServiceForTest(SomeService someService) {
        this.someService = someService;
    }

    public String methodForTest (String s) {
        return someService.generateString(s);
    }

}

class 测试 groovy, Spock 框架

class ServiceForTestTest extends Specification {

    ServiceForTest serviceForTest

    void setup(){
        SomeService someServiceMock = Mock(SomeService)
        someServiceMock.generateString("TEST") >> "TEST"
        serviceForTest = new ServiceForTest(someServiceMock)
    }

    def "Test for return current value"(){

        when:
        def methodForTest = serviceForTest.methodForTest("TEST")

        then:
        methodForTest == "TEST"

    }
}

您使用 @Autowired,即某种依赖注入框架,例如 Spring 或 Java EE CDI。这些框架有测试支持。 Spock 专门针对 Spring 测试提供了一个 Spring module 供您使用。我不是 Spring 用户,所以我不能告诉你具体怎么做,但是文档非常好。

作为一般性答案,即使没有任何框架支持,您也可以轻松地进行测试,只要您按照惯例将测试放入与被测 class 相同的包中。因为您要向其中注入模拟的字段是 protected,所以对于 JVM 来说,所有子 classes 以及同一包中的其他 classes 都可以访问它。即,您可以简单地设置值:

serviceForTest = new ServiceForTest()
serviceForTest.someService = someServiceMock

或者,更优雅地使用隐式设置字段值的 Groovy-style 构造函数:

serviceForTest = new ServiceForTest(someService: someServiceMock)

一般来说,我推荐构造函数或 setter 注入而不是依赖字段注入(尤其是当字段是私有的时候),因为那样的话在可测试性方面你对你的 DI 框架有严格的依赖并且不容易写单元测试。所以如果你能重构,我建议你去做。您刚刚注意到,测试这些东西可能有点令人头疼,除非您有像在受保护字段的这种特殊情况下那样的出路。但这不是那么超级refactoring-friendly.