模拟Java枚举以添加一个值以测试失败情况
||
我有一个或多或少像这样的枚举开关:
public static enum MyEnum {A, B}
public int foo(MyEnum value) {
switch(value) {
case(A): return calculateSomething();
case(B): return calculateSomethingElse();
}
throw new IllegalArgumentException(\"Do not know how to handle \" + value);
}
并且我希望测试涵盖所有行,但是由于期望代码能够处理所有可能性,因此如果没有在开关中使用相应的case语句,则无法提供值。
扩展枚举以添加额外的值是不可能的,并且仅模拟equals方法以返回ѭ1也不可行,因为生成的字节码使用幕后的跳转表进行适当处理...所以我我曾想过,用PowerMock或其他方法可以实现一些黑魔法。
谢谢!
编辑:
当我拥有枚举时,我以为我可以在值上添加一个方法,从而完全避免切换问题。但我还是要提这个问题,因为它仍然很有趣。
没有找到相关结果
已邀请:
8 个回复
姬第柔炒
这是具有完整代码覆盖率的单元测试,该测试适用于Powermock(1.4.10),Mockito(1.8.5)和JUnit(4.8.2):
结果:
箩冀娥
中的最后一行,我将其删除并依赖于静态代码分析。例如,IntelliJ IDEA具有\“ Enum
语句缺少大小写\”代码检查,如果缺少
,则将对
方法产生警告。
坍锭嘉韭蓝
钾涎净介
董碘奴星
和
,并且您已经有了新的枚举实例。 使用Spock框架进行测试,结果如下所示:
如果您还希望
方法返回新的枚举,则现在可以使用JMockit模拟
调用,例如
或者您可以再次使用普通的旧反射来操作the16ѭ字段,例如:
只要您不使用
表达式,但使用某些
或类似表达式,则仅第一部分或第一部分和第二部分就足够了。 但是,如果要处理
表达式,则e。 G。想要对21例进行100%的覆盖,在枚举像您的示例那样扩展的情况下引发异常,事情会变得更加复杂,同时也更加容易。 稍微复杂一点,因为您需要认真思考一下以操作编译器在编译器生成的合成匿名innerner类中生成的合成字段,因此您所做的工作并不十分明显,并且您必须绑定到实际实现中编译器,因此这可能会在任何Java版本中随时中断,即使您对同一Java版本使用不同的编译器也是如此。实际上,Java 6和Java 8之间已经有所不同。 稍微容易一点,因为您可以忘记此答案的前两个部分,因为根本不需要创建新的枚举实例,只需要操纵
,就必须操纵ѭ22测试你想要的。 我最近在https://www.javaspecialists.eu/archive/Issue161.html上找到了一篇很好的文章。 那里的大多数信息仍然有效,只是现在包含开关映射的内部类不再是命名的内部类,而是匿名类,因此您不能再使用
,而需要使用下面显示的其他方法。 基本上总结起来,在字节码级别启用开关不适用于枚举,而仅适用于整数。因此,编译器要做的是,它创建一个匿名内部类(以前按本文撰写,是一个命名内部类,这是Java 6 vs. Java 8),其中包含一个静态最终
字段,称为
,该字段由整数1填充, 2,3,...位于
值的索引处。 这意味着当代码到达实际的开关时,
如果现在
将具有在第一步中创建的value29ѭ值,或者将
设置为大于编译器生成的数组的某个值,则将得到get30ѭ,否则将得到其他开关情况值之一,在两种情况下,这都无助于测试通缉的“ 21”案。 现在,您可以获取此
字段并进行修复,以包含
枚举实例的序数的映射。但是正如我之前说的,对于这个用例,测试“ 21”例,您根本不需要前两个步骤。相反,您可以简单地将任何现有的枚举实例提供给被测代码,并只需操作映射“ 22”,就可以触发“ 21”的情况。 因此,对于这个测试用例,所有必要的实际上就是这个,再次用Spock(Groovy)代码编写,但是您也可以轻松地使其适应Java:
在这种情况下,您根本不需要任何模拟框架。实际上,无论如何它还是无济于事,因为我所知道的模拟框架都不允许您模拟数组访问。您可以使用JMockit或任何模拟框架来模拟
的返回值,但这将再次导致不同的开关分支或AIOOBE。 我刚刚显示的这段代码的作用是: 它遍历包含switch表达式的类内的匿名类 在那些搜索带有开关图的字段中 如果未找到该字段,则尝试下一个类 如果
抛出
,则测试将失败,这是有意的,因为这意味着您使用遵循不同策略或命名模式的编译器编译了代码,因此您需要添加更多的知识以涵盖用于以下方面的不同编译器策略:开启枚举值。因为如果找到带有该字段的类,则ѭ42会离开for循环,并且测试可以继续。当然,整个策略取决于匿名类从1开始编号且没有空格,但是我希望这是一个非常安全的假设。如果您不是在使用编译器,则需要相应地修改搜索算法。 如果找到switch映射字段,则会创建一个相同大小的新int数组 新数组填充为
,只要您没有包含2,147,483,647值的枚举,通常应触发
情况 新数组将分配给switch映射字段 使用loop42保留for循环 现在可以完成实际测试,触发要评估的开关表达式 最后(如果不使用Spock,则在
块中;如果使用Spock则在
块中)以确保这不会影响同一类的其他测试,将原始开关图放回开关图字段中
孤捷侩
所以我在枚举HttpMethod中总共有5个序数,但是mockito不知道它.Mockito始终创建模拟数据及其null,您最终将传递null值。 因此,这里提出了一种解决方案,您可以将序数随机化并获得正确的枚举,该枚举可以传递给其他测试
输出:
磋判粗惊
良阑纠苫