我有 util 方法来处理一些文本。处理方式由
enum Mode
决定。在编写单元测试来覆盖此方法时,我必须添加虚拟 enum
元素,以便将 default
语句的 switch
部分包含到测试中。默认部分是我检查 enum
是否被新元素扩展并且该新元素未添加到 switch
语句中的情况,这意味着该新元素无法进行处理。如果我删除虚拟元素并尝试使用 null
,那么我会得到 NPE,并且 Java 甚至不会输入 switch
语句 - 这是当您使用 null
表示 switch
语句时的正常行为。
问题: 有没有办法避免在单元测试中添加虚拟
enum
元素,但仍覆盖 default
语句的 switch
部分?
为了清晰起见,添加了:Java 17、JUnit Jupiter 5、Eclipse 代码覆盖工具
代码
abstract class LineBreaker {
public enum Mode {
LAST_SPACES,
LAST_CHARS,
DUMMY
}
public static String process(
final String text,
final LineBreaker.Mode mode,
final int amount
) {
switch (mode) {
case LAST_SPACES : {
// blah blah create `processingResult`
return processingResult;
}
case LAST_CHARS : {
// blah blah create `processingResult`
return processingResult;
}
default :
throw new IllegalStateException(
"Processor for " +
LineBreaker.class.getSimpleName() + "." +
LineBreaker.Mode.class.getSimpleName() + "." +
mode.name() +
" not implemented"
);
}
}
}
单元测试
@Test
void testLineBreakerException() {
assertThrows(
IllegalStateException.class,
() -> LineBreaker.process( "Dog", LineBreaker.Mode.DUMMY, 10 ),
"Expected " + IllegalStateException.class.getSimpleName()
);
}
无需额外虚拟枚举元素的单元测试
无需额外的虚拟枚举元素即可实现代码覆盖率
使用附加虚拟枚举元素进行单元测试
无需额外的虚拟枚举元素即可实现代码覆盖率
您不需要创建或扩展现有的
enum Mode
。你只需要使用 Mockito 来模拟这个枚举即可。testImplementation 组:'org.mockito',名称:'mockito-core',版本:'5.11.0'
策略是创建一个模拟枚举
UNSUPPORTED
。使用 Mockito.mockStatic
模拟枚举的静态方法 values()
,并返回现有值加上模拟枚举 UNSUPPORTED
。UNSUPPORTED
枚举将触发 switch-case 语句的默认路径,这使您可以轻松地执行断言。
代码片段
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doReturn;
public class SOEnumQuestionTest {
public enum Mode {
LAST_SPACES,
LAST_CHARS
}
public static String process(final Mode mode) {
switch (mode) {
case LAST_SPACES : {
return "processing last spaces";
}
case LAST_CHARS : {
return "processing last chars";
}
default :
throw new IllegalStateException("Processor not implemented");
}
}
@Test
public void testProcessWithDefaultSwitchCase() {
try (MockedStatic<Mode> dummyModeMockedStatic = Mockito.mockStatic(Mode.class)) {
final Mode UNSUPPORTED = Mockito.mock(Mode.class);
// We want to give the mocked enum in the last position of the enum values list
// E.g: Enum has 2 values -> our mock UNSUPPORTED enum index is 2
doReturn(2).when(UNSUPPORTED).ordinal();
dummyModeMockedStatic.when(Mode::values)
.thenReturn(new Mode[]{
Mode.LAST_SPACES,
Mode.LAST_CHARS,
UNSUPPORTED});
Throwable expectedException = assertThrows(IllegalStateException.class, () -> SOEnumQuestionTest.process(UNSUPPORTED));
assertEquals("Processor not implemented", expectedException.getMessage());
}
}
}
由于您使用的是 Java 17 并且您的分支正在返回一个值,因此您可以使用 switch 表达式,它必须是详尽的(因此编译器将检查您是否涵盖了所有情况)。
public static String process(
final String text,
final LineBreaker.Mode mode,
final int amount
) {
String result = switch (mode) {
case LAST_SPACES -> "some processing result";
case LAST_CHARS -> "another result";
};
return result;
}
这样,您就不需要默认分支或测试。