使用Mockito来模拟一个枚举类型?

15

我需要模拟以下枚举:

public enum PersonStatus
{
    WORKING,
    HOLIDAY,
    SICK      
}

这是因为它被用于我正在测试的以下类中:

被测试的类:


public interface PersonRepository extends CrudRepository<Person, Integer>
{
    List<Person> findByStatus(PersonStatus personStatus);
}

这是我的当前测试尝试:

当前测试:

public class PersonRepositoryTest {

    private final Logger LOGGER = LoggerFactory.getLogger(PersonRepositoryTest.class);

    //Mock the PersonRepository class
    @Mock
    private PersonRepository PersonRepository;

    @Mock
    private PersonStatus personStatus;

    @Before
    public void setUp() throws Exception {

        MockitoAnnotations.initMocks(this);
        assertThat(PersonRepository, notNullValue());
        assertThat(PersonStatus, notNullValue());
    }

    @Test
    public void testFindByStatus() throws ParseException {

        List<Person> personlist = PersonRepository.findByStatus(personStatus);
        assertThat(personlist, notNullValue());
    }
}

这将会导致以下错误:

错误:

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class PersonStatus
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types

我该如何解决这个问题?


2
你确定需要模拟“PersonStatus”吗?难道你不能直接使用实际的实例吗? - khelwood
我认为你不想嘲笑枚举 - 你想在测试中传递枚举的各种值,并检查结果是否符合预期。 - assylias
@assylias,请提供答案,展示如何这样做。 - java123999
为什么你想要模拟枚举? - Hiren
1
你只需要像 PersonStatus.WORKING 或者 PersonStatus.SICK 这样传递参数,然后将其与您所期望的结果进行检查。 - Hiren
显示剩余2条评论
3个回答

16

为了完整地呈现情况:

Mockito 2的最新版本非常好地支持对final类进行mocking。但是您必须首先显式启用此新的实验功能!

(请参见此处有关如何执行此操作的说明 - 它归结为向类路径添加一个mockito-extensions/org.mockito.plugins.MockMaker文件,其中包含值mock-maker-inline

当然:只有在必要时才会进行mocking。您想mock Enum实例的愿望很可能是由于不理解 - 或者因为您在这里创建了难以测试的代码。在这种意义上,真正的答案将是探索避免这种mocking的方法。


2
尽管 OP 的情况可以在不模拟枚举的情况下处理,但这应该是被接受的答案。 - saran3h
有过同样的情况,最终我将所需的枚举信息传递给方法,以便该方法可以使用我可以操作的参数进行测试。从 fooMethod(FooEnum myEnum) 更改为 fooMethod(String data1FromEnum, Stirng data2fromEnum)。现在更容易测试,但在我看来不太干净,特别是如果您有很多参数的话。如果您有很多参数,最好使用 DTO 对象,您也可以像这样模拟:fooMethod(DtoClassWithEnumVars dto); - Given

6

您的testFindByStatus试图断言findByStatus方法不会返回null。

如果该方法在personStatus参数的值不同的情况下都能正常工作,只需传递其中一个值即可:

@Test
public void testFindByStatus() throws ParseException {
    List<Person> personlist = PersonRepository.findByStatus(WORKING);
    assertThat(personlist, notNullValue());
}

如果行为可能与其他可能的值不同,您可以测试每个值:

@Test
public void testFindByStatus() throws ParseException {
    for (PersonStatus status : PersonStatus.values()) {
        List<Person> personlist = PersonRepository.findByStatus(status);
        assertThat(personlist, notNullValue());
    }
}

你是如何解决 org.mockito.exceptions.base.MockitoException 的问题的?我从你的回答中没有理解清楚。 - Alessandro C
@AlessandroC OP试图模拟一个枚举,这导致了异常的触发。在我的答案中,我建议不要模拟枚举,这样问题就得到了解决。 - assylias
1
啊,好的...不幸的是这并没有解决我的问题,但还是谢谢您的反馈! - Alessandro C

5

如已提到的,使用Mockito 2并启用实验性功能。

实际上缺少的是一个示例片段来演示如何操作。 考虑一个名为LicenseHistoryAction的枚举类型,其中已存在4个值,这将正确地模拟一个UNSUPPORTED值:

try (MockedStatic<LicenseHistoryAction> licenseHistoryActionMockedStatic = Mockito.mockStatic(LicenseHistoryAction.class)) {
    final LicenseHistoryAction UNSUPPORTED = Mockito.mock(LicenseHistoryAction.class);
    Mockito.doReturn(4).when(UNSUPPORTED).ordinal();

    licenseHistoryActionMockedStatic.when(LicenseHistoryAction::values)
            .thenReturn(new LicenseHistoryAction[]{
                    LicenseHistoryAction.ASSIGN,
                    LicenseHistoryAction.RELEASE,
                    LicenseHistoryAction.UNBIND,
                    LicenseHistoryAction.DENY,
                    UNSUPPORTED});
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接