6

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);
}
Community
  • 1
  • 1
Ben Green
  • 3,953
  • 3
  • 29
  • 49

3 Answers3

6

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.

Opal
  • 81,889
  • 28
  • 189
  • 210
  • Thanks. I've actually already got this test. As I say, only struggling with the testing of the default block. – Ben Green Mar 14 '15 at 11:41
  • @BenGreen, sure. As I said will return to it later on. However spock doesn't support mocking enums. – Opal Mar 14 '15 at 11:54
  • @BenGreen, I've tried to use `EnumBuster`: http://www.javaspecialists.eu/archive/Issue161.html, it works perfectly with java however have some problems with groovy. I won't fix it in a reasonable time so you can consider my answer as being final for now. – Opal Mar 15 '15 at 09:25
  • Ok thanks. I'll accept your answer based on the "Spock doesn't support mocking enums" bit. Thanks for all the work. – Ben Green Mar 15 '15 at 09:30
0

This question is old and I was thinking about how to cover the default: branch. I also looked at the EnumBuster workaround mentioned in a comment. Here is an EnumBuster update for more recent Java versions, by the way.

Anyway, I think that using tools - I would rather call it dirty tricks - like that is not a particularly good idea. Probably, adding a static code analysis tool covering that case to your build configuration would be better. I quickly checked the default rule sets for Checkstyle, PMD, SonarQube and FindBugs, but did not find rules exactly doing what we need here, but both Eclipse and IntelliJ IDEA have built-in inspections for it. They are also configurable, i.e. to issue warnings or build errors, to still warn/fail if there is a default: clause or not:

Eclipse inspection

IDEA inspection

Maybe somewhere out there is a custom rule for one of the popular static code analysis tools which I just did not find. Of course you could also write a custom rule. The advantage would be that it would work for the whole project or potentially many projects, be re-usable and would not require an extra test.

If you are obsessed with test coverage for this corner case, the static code analysis tool would not help you, of course. Then you could still put a custom version of the enum with an extra dummy value on your test class path (possibly auto-generated as a derivation of the original source file) in order to cover the default case or an exception thrown and otherwise never reached after a switch block.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
0

As explained here https://www.baeldung.com/java-extending-enums you can create an interface for the Enum and mock the interface.

ejaenv
  • 2,117
  • 1
  • 23
  • 28