Skip to content
Advertisement

How to mock a class that has a method that returns nothing, using Spock

I am currently attempting to set up Spock testing for a class that I created that has a method that returns nothing.

public final class Test implements ITest {

    private static Logger logger = LoggerFactory.getLogger(test.class);

    private DataSource testSource;

    public Test(DataSource testSource) {
        testSource = testSource;
    }

    public void validateFile() {

        if (logger.isDebugEnabled()) {
            logger.debug("validating file");
        }

        Handler handler = new Handler();
        handler.getValues(testSource);
    }
}

Above is the test I want to Spock test, and I only want to test this class and not the Handler class that has the method getValues.

This is my test mock class:

def "test buildConfig"() {
    given:
    DataSource testSource = Mock()
    Handler handler = Mock()
    Test test = new Test(testSource)
    when:
    test.validateFile()
    then:
    1 * handler.getValues(_)
}

But I get a null pointer exception, because handler.getValues does some logic in that method that I have not provided, it seems. But I don’t care about the class: I just want to test that when I run validateFile method in test class, it creates a Handler and calls the getValues method on the handler.

Advertisement

Answer

Because your class creates its own new Handler(), it’s not using the Mock you created.

To be able to verify this, you’ll need to refactor your class to be more testable.

Pass in the Handler

First, let’s look at passing a Handler into the method:

public final class Test implements ITest {

    private static Logger logger = LoggerFactory.getLogger(test.class);

    private DataSource testSource;

    public Test(DataSource testSource) {
        testSource = testSource;
    }

    public void validateFile(Handler handler) {

        if (logger.isDebugEnabled()) {
            logger.debug("validating file");
        }

        handler.getValues(testSource);
    }
}

Now we can pass our mock into our validateFile call and verify it successfully:

def "test buildConfig"() {
    given: 'a datasource, handler and Test'
    DataSource testSource = Mock()
    Handler handler = Mock()
    Test test = new Test(testSource)

    when: 'we validate our file'
    test.validateFile(handler)

    then: 'our handler was invoked'
    1 * handler.getValues(_)
}

Use a Supplier

If we really want the Handler to be created inside the method, then we can pass in a Supplier<Handler> as per the comment from @leonard-brünings

As an alternative to changing the verifyFile() signature, this time, let’s rewrite our constructor to take a Supplier as an argument and store our function as a field:

public final class Test implements ITest {

    private static Logger logger = LoggerFactory.getLogger(test.class);

    private DataSource testSource;
    private Supplier<Handler> supplierOfHandler;

    public Test(DataSource testSource, Supplier<Handler> handlerSupplier) 
    {
        testSource = testSource;
        supplierOfHandler = handlerSupplier;
    }

    public void validateFile() {

        if (logger.isDebugEnabled()) {
            logger.debug("validating file");
        }
        handler = supplierOfHandler.get()
        handler.getValues(testSource);
    }
}

… and then set up our test with a supplier that returns a Mock Handler, that we can then use to verify interactions:

def "test buildConfig"() {
    given: 'a datasource, handler and Test'
    DataSource testSource = Mock()
    Handler handler = Mock()
    Test test = new Test(testSource, () -> handler)

    when: 'we validate our file'
    test.validateFile()

    then: 'our handler was invoked'
    1 * handler.getValues(_)
}

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