Skip to content
Advertisement

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

Colleagues, I welcome you all! Tell me how to decide, or how to act. (Java11, SpringBoot, testing – Spock Framework) I need to write a test that will test a class method, the whole problem is that the method of the class under test calls another service through inheritance, which is not declared in the class under test, but in its abstract ancestor. How to test such a story? If this service were declared in the class under test itself, then everything is clear, I would create a mock in the test and pass it to the constructor, but what if this service is located at the ancestor? I am attaching an example code below.

// 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;
    }

}

And below is an example of what I would do if the service was in the class being tested

//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 test groovy, Spock Framework

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"

    }
}

Advertisement

Answer

You use @Autowired, i.e. some kind of dependency injection framework such as Spring or Java EE CDI. Those frameworks have testing support. Specifically for Spring testing, Spock has a Spring module which you can use. I am not a Spring user, so I cannot tell you how to exactly do that, but the documentation is pretty good.

As a general answer, even without any framework support you can test this easily, if you follow the convention to put the test into the same package as the class under test. Because the field you want to inject a mock into is protected, it means for the JVM that all subclasses, but also other classes in the same package have access to it. I.e., you can simply set the value:

serviceForTest = new ServiceForTest()
serviceForTest.someService = someServiceMock

Or, more elegantly using a Groovy-style constructor which implicitly sets field values:

serviceForTest = new ServiceForTest(someService: someServiceMock)

Generally, I recommend constructor or setter injection rather than relying on field injection (especially when fields are private), because then with regard to testability you have a strict dependency to your DI framework and cannot easily write unit tests. So if you can refactor, I recommend you to do it. You just noticed that testing such things can be kind of a headache, unless you have a way out like in this particular case with the protected field. But that is not so super refactoring-friendly.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement