Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to mock an enum with Spock?

I have a switch statement to handle an java enum foo, and am using spock to write some groovy unit tests. I have already added a test which verifies that every type of foo is currently handled without throwing an exception. Now I want to test that an unrecognised type of foo will cause an exception to be thrown.

To do this I will have to mock an enum, and have already seen the solution outlined here: Mocking Java enum to add a value to test fail case

I also know that it is possible to do with powermock, but I really like spock, as I find it incredibly lightweight and so was looking for a solution using spock.

I thought something like this might have worked:

    def "An unexpected type of foo causes an exception to be thrown"() {
        given:
        Foo foo = Mock()
        when:
        subjectUnderTest.handleFoo foo
        then:
        thrown Exception
}

However, this fails with the following error message: org.spockframework.mock.CannotCreateMockException: Cannot create mock for class com.Foo because Java mocks cannot mock final classes. If the code under test is written in Groovy, use a Groovy mock.

I was wondering if anybody knew of a way to do this using spock, as I can't find a solution on any documentation.

Edit After some of the comments below, I feel it is best to clarify why I want to write these tests.

I work in a team with quite a few developers, and it is quite possible that one of them will update the enum. I want tests to fail if this happens to make the developer aware that they need to add logic to handle the new enum type. For this, I wrote a test that iterates over every possible value that the enum could have, passed it into the method and verify that no exception is thrown. Now, if a user added a new enum, this test would fail as there is nothing to handle it so an exception would (hopefully) be thrown.

The second test (the one I am struggling to write) is to clarify that the default logic works as I would expect it to, and that an exception does in fact get thrown. The only way I can think of doing this without creating an enum value that I will never want to be used is to mock an enum, and test that an exception gets thrown if the enum value is not handled.

The code looks like this:

Enum Foo:

public enum Foo {
    ONE, TWO;
}

Method to handle foo:

private void handleFoo(Foo foo) {
    switch (foo) {
        case ONE:
           doEventOne();
           break;
        case TWO:
           doEventTwo()
           break;
        default:
           throw new IllegalArgumentException("Do not know how to handle " + foo);
}
like image 268
Ben Green Avatar asked Mar 13 '15 14:03

Ben Green


People also ask

Can we mock an enum?

Extending the enum to add an extra value is not possible, and just mocking the equals method to return false won't work either because the bytecode generated uses a jump table behind the curtains to go to the proper case...

Can you loop through enums?

Enums don't have methods for iteration, like forEach() or iterator(). Instead, we can use the array of the Enum values returned by the values() method.


1 Answers

The following specification will cover adding new enum without providing service logic for it:

def 'no exception thrown for all enums'() {
    given:
    def service = new SampleService()

    when:
    service.handleFoo(se)

    then:
    noExceptionThrown()

    where:
    se << SampleEnum.values()
}

I've tried to write a test that will mock the enum or add another value in test runtime, however failed for now. Will back to it later on.

Spock doesn't support mocking enums.

like image 137
Opal Avatar answered Oct 19 '22 23:10

Opal