简体   繁体   English

如何模拟ENUM类中的方法?

[英]How to mock a method in an ENUM class?

I am working on writing JUNIT test case for my below ENUm class. 我正在为我的ENUm课程编写JUNIT测试用例。 My below class will only give me the hostname for the current machine where I am running my code. 我的下面的类只会给我当前运行我的代码的机器的主机名。 While I am writing JUNIT test, how can I mock the below class, so that I can change getHostName() method whenever I want to so that whenever I am calling getDatacenter() , it can return me whatever hostname I am passing by mocking it. 在我编写JUNIT测试时,如何模拟下面的类,这样我可以随时更改getHostName()方法,这样每当我调用getDatacenter() ,它都可以通过getDatacenter()它返回我传递的任何主机名。 I don't want to make it as a parametrized. 我不想把它作为参数化的。

I just want to test certain cases while changing the hostname while mocking it. 我只想在模拟它的同时更改主机名时测试某些情况。

public enum DatacenterEnum {
    DEV, DC1, DC2, DC3;


    public static String forCode(int code) {
    return (code >= 0 && code < values().length) ? values()[code].name() : null;
    }
    private static final String getHostName() {
        try {
            return InetAddress.getLocalHost().getCanonicalHostName().toLowerCase();
        } catch (UnknownHostException e) {
            s_logger.logError("error = ", e);
        }

        return null;
    }

    public static String getDatacenter() {
        return getHostName();
    }
}

It is possible, but this is not recommended , it would be better to refactor the code. 这是可能的,但不建议这样做,重构代码会更好。

Working example with Mockito/PowerMock Mockito / PowerMock的工作示例

@RunWith(PowerMockRunner.class)
@PrepareForTest(DatacenterEnum.class)
public class DatacenterEnumTest {

    @Mock
    InetAddress inetAddress;

    @Test
    public void shouldReturnDatacenter() throws UnknownHostException {
        //given
        mockStatic(InetAddress.class);
        given(inetAddress.getCanonicalHostName()).willReturn("foo");
        given(InetAddress.getLocalHost()).willReturn(inetAddress);

        //when
        String datacenter = DatacenterEnum.getDatacenter();

        //then
        assertThat(datacenter).isEqualTo("foo");
    }
}

Dependencies 依赖

  • org.powermock:powermock-module-junit4:1.5.2 org.powermock:powermock模块-junit4:1.5.2
  • org.powermock:powermock-api-mockito:1.5.2 org.powermock:powermock-API的Mockito:1.5.2
  • org.assertj:assertj-core:1.5.0 org.assertj:assertj核心:1.5.0
  • junit:junit:4.11 JUnit的:JUnit的:4.11

You could create a Datacenter interface and have the enum implement the interface. 您可以创建数据中心接口并让枚举实现接口。 This would make mocking more easy. 这将使模拟更容易。

Most of all I would not place configuration information in an Enum to begin with. 最重要的是,我不会在Enum中放置配置信息。 If you ever have to add an other Datacenter (or the config of a Datacenter changes) you have to recompile the code. 如果您必须添加其他数据中心(或数据中心更改的配置),则必须重新编译代码。 Consider putting the configuration in a normal class reading for example a java properties file or a XML file. 考虑将配置放在普通的类读取中,例如java属性文件或XML文件。 (This function might be already implement in your framework.) (此功能可能已在您的框架中实现。)

If this is not possible you might use "darkes reflaction" magic to change fields in your Enum to the required values. 如果这不可能,您可以使用“darkes reflaction”magic将Enum中的字段更改为所需的值。

It's easy with JMockit: 使用JMockit很容易:

@Test
public void mockInetAddress(@Cascading final InetAddress inetAddress)
{
    new NonStrictExpectations() {{
        inetAddress.getCanonicalHostName(); result = "foo";
    }};

    String datacenter = DatacenterEnum.getDatacenter();

    assertEquals("foo", datacenter);
}

You could also mock the getHostName() method in the enum, of course, but it's best to avoid mocking private methods. 当然,您也可以在枚举中模拟getHostName()方法,但最好避免模拟private方法。

This is a way you can do it with Mockito/Powermock. 这是Mockito / Powermock可以做到的一种方式。 You need Powermock, because Mockito is not able to mock static mehtods: 你需要Powermock,因为Mockito无法模拟静态mehtods:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;

@RunWith(PowerMockRunner.class)
@PrepareForTest({DatacenterEnum.class})
public class DatacenterEnumTest {

    @Test
    public void testGetDatacenter() {
        mockStatic(DatacenterEnum.class);
        when(DatacenterEnum.getDatacenter()).thenReturn("YourHostname");

        String datacenter = DatacenterEnum.getDatacenter();

        assertEquals("YourHostname", datacenter);
    }
}

Maven Dependencies Maven依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito</artifactId>
        <version>1.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>1.5.2</version>
    </dependency>
</dependencies>

I may be old school, but I'd really refactor the code under test rather than using classloader hacks. 我可能是老上学,但我确实重构了测试中的代码,而不是使用classloader hacks。 Something like: 就像是:

public enum DatacenterEnum {
    DEV, DC1, DC2, DC3;


    static String hostName = InetAddress.getLocalHost().getCanonicalHostName().toLowerCase();

    public static String getHostName() {
        return hostName;
    }
}

and in your test code, prior to running the test: 在测试代​​码中,在运行测试之前:

DataCenterEnum.hostName = "foo";

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM