是否可以用 Spock 模拟枚举?

Is it possible to mock an enum with Spock?

我有一个 switch 语句来处理 java 枚举 f​​oo,并且正在使用 spock 编写一些 groovy 单元测试。我已经添加了一个测试来验证当前是否处理了每种类型的 foo 而没有抛出异常。现在我想测试一个无法识别的 foo 类型会导致抛出异常。

为此,我将不得不模拟一个枚举,并且已经看到此处概述的解决方案: Mocking Java enum to add a value to test fail case

我也知道可以使用 powermock,但我真的很喜欢 spock,因为我发现它非常轻巧,所以一直在寻找使用 spock 的解决方案。

我认为这样的方法可能有效:

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

但是,此操作失败并显示以下错误消息: 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.

我想知道是否有人知道使用 spock 执行此操作的方法,因为我在任何文档中都找不到解决方案。

编辑 在下面的一些评论之后,我觉得最好澄清一下我为什么要写这些测试。

我在一个有很多开发人员的团队中工作,他们中的一个很可能会更新枚举。如果这让开发人员意识到他们需要添加逻辑来处理新的枚举类型,我希望测试失败。 为此,我编写了一个测试,遍历枚举可能具有的每个可能值,将其传递给方法并验证没有抛出异常。现在,如果用户添加了一个新的枚举,这个测试将失败,因为没有任何东西可以处理它,所以(希望)会抛出一个异常。

第二个测试(我正在努力编写的那个)是为了阐明默认逻辑是否按我预期的那样工作,并且确实会抛出异常。在不创建我永远不想使用的枚举值的情况下,我能想到的唯一方法是模拟枚举,并测试如果未处理枚举值是否会抛出异常。

代码如下所示:

枚举 Foo:

public enum Foo {
    ONE, TWO;
}

处理 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);
}

以下规范将涵盖添加新枚举而不为其提供服务逻辑:

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

    when:
    service.handleFoo(se)

    then:
    noExceptionThrown()

    where:
    se << SampleEnum.values()
}

我尝试编写一个测试来模拟枚举或在测试运行时添加另一个值,但暂时失败了。稍后再回来。

Spock 不支持模拟枚举。

这个问题很老了,我在想如何覆盖default:分支。我还查看了评论中提到的 EnumBuster 解决方法。顺便说一下,这里是最近 Java 版本的 EnumBuster update

无论如何,我认为使用工具——我宁愿称之为肮脏的把戏——并不是一个特别好的主意。可能,将涵盖该案例的静态代码分析工具添加到您的构建配置中会更好。我快速检查了 Checkstyle、PMD、SonarQube 和 FindBugs 的默认规则集,但没有找到完全符合我们需要的规则,但 Eclipse 和 IntelliJ IDEA 都内置了对它的检查。它们也是可配置的,即发出警告或构建错误,仍然 warn/fail 如果有 default: 子句或不:

也许在某个地方有一个我只是没有找到的流行静态代码分析工具的自定义规则。当然你也可以写一个自定义规则。优点是它适用于整个项目或可能的许多项目,可重复使用并且不需要额外测试。

如果您对这种极端情况的测试覆盖率很着迷,那么静态代码分析工具当然帮不了您。然后你仍然可以在你的测试 class 路径上放置一个带有额外虚拟值的自定义版本的枚举(可能作为原始源文件的派生自动生成)以覆盖默认情况或抛出的异常否则在切换块之后永远不会到达。

如此处所述https://www.baeldung.com/java-extending-enums 您可以为枚举创建一个接口并模拟该接口。