3
votes

I'm using a Spock test written in Groovy to test some Java code. I'm using JMockit to mock methods in the java code, as Spock only handles mocking Groovy classes. I'm running into a problem where a JMockit MockUp is persisting between tests. Such a mock instance should only exist for the test (per JMockit documentation), but this isn't working, and I imagine it's because it's not using the JMockit test runner, and rather the Spock test runner.

Here is the simplest example of the problem I'm facing. I have a simple method returning a string, I can change the return value of the method with MockUp but it still exists for the third test, which doesn't expect it to be used.

Java Class

public class ClassToTest {
    public String method() {
        return "original";
    }
}

Spock Test

class ClassToTestSpec extends Specification {
    void "first test"() {
        when:
        String result = new ClassToTest().method()

        then:
        result == "original"
    }

    void "second test"() {
        setup:
        new MockUp<ClassToTest>() {
            @Mock
            public String method() {
                return "mocked"
            }
        }

        when:
        String result = new ClassToTest().method()

        then:
        result == "mocked"
    }

    void "third test"() {
        when:
        String result = new ClassToTest().method()

        then:
        result == "original"
    }
}

The third test fails, because ClassToTest.method() still returns the String "mocked" rather than "original". Using a debugger I have validated that the Mocked method is called twice.

Question

Is there any way to manually remove a class MockUp in JMockit? Thanks.

2
Spock can mock both Java and Groovy classes. Mocking the class under test is a smell, and should be used with care. - Peter Niederwieser
Thanks for the response @PeterNiederwieser, I'm not able to use the normal notation, instance.method() >> result, can you point me in the proper direction? - mnd
Scratch that, the notation does work, I had forgotten to instantiate the object with Mock, such as ClassToTest instance = Mock(ClassToTest). Now to figure out how to make this a partial mock, possibly with a Spy. - mnd
If you absolutely need/want a half-mock, Spy() is the way to go. Often it's better to refactor the code under test or reconsider what/how to test. - Peter Niederwieser

2 Answers

3
votes

You can call the MockUp.tearDown method on the created mockup object, to manually undo its effects.

1
votes

Not exactly an answer to the question - because I still don't know if JMockit's MockUp can be manually removed. But thanks to @PeterNiederwieser's comments, I found that you can actually create a partial mock for a Java class. Below is the change to the second test from above.

void "second test"() {
    setup:
    ClassToTest test = Spy(ClassToTest) {
        method() >> "mocked"
    }

    when:
    String result = test.method()

    then:
    result == "mocked"
}

Peter mentioned reconsidering how and what to test if a Spy() is necessary, but for my use case this is preferred.