12

I am working on writing JUNIT test case for my below ENUm class. 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. 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();
    }
}
  • 1
    Do those method have to be in the enum class? There doesn't appear to be any references between the enum values and those methods. – Peter Lawrey Jan 18 '14 at 10:01
  • I have removed those information from the code.. There are couple of methods which are using those details accordingly from the hostname.. So my main center of attraction was mocking the getHostName method, around which other codes revovlve.. –  Jan 18 '14 at 10:05
  • http://stackoverflow.com/a/9722244/516167 – MariuszS Jan 18 '14 at 11:18

5 Answers5

3

It is possible, but this is not recommended, it would be better to refactor the code.

Working example with 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-api-mockito:1.5.2
  • org.assertj:assertj-core:1.5.0
  • junit:junit:4.11
MariuszS
  • 30,646
  • 12
  • 114
  • 155
1

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. 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. (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.

Akhil KC
  • 76
  • 1
  • 8
Tobias Kremer
  • 1,531
  • 2
  • 15
  • 21
1

It's easy with 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.

Rogério
  • 16,171
  • 2
  • 50
  • 63
0

This is a way you can do it with Mockito/Powermock. You need Powermock, because Mockito is not able to mock static 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

<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>
Philipp Hofmann
  • 3,388
  • 26
  • 32
0

I may be old school, but I'd really refactor the code under test rather than using 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";
meriton
  • 68,356
  • 14
  • 108
  • 175
  • 1
    This will only partially work. The rules of the `ClassLoader` are to load a class on it's first reference, which entails static initializers. The OP has an `UnknownHostException` being thrown, and now that you've moved it to a static field initialization, that initialization now has the chance for an exception to propagate up to the `ClassLoader`. Your future assignment to `hostName` will result in a `NoClassDefFoundError`, if some sort of checked or unchecked exception were to be thrown. It's not a good idea to put methods that throw exceptions in unchecked initializers. – searchengine27 Apr 20 '18 at 20:45
  • Perhaps I was overzealous in removing that catch block, but I could not imagine a case where the local host name was not known to DNS? But yes, if you actually see this exception thrown, I'd move the lookup into a dedicated static method and add a catch block, so the class can correctly initialize and the test code gets a chance to override the host name. – meriton Apr 21 '18 at 00:47
  • Well it's the `InetAddress#getLocalHost()` function itself that throws the exception, and the javadocs only seem to indicate `if the local host name could not be resolved into an address.` But in fairness, I didn't really think past that point myself to think why that could occur. But it does happen in cases: https://stackoverflow.com/questions/1881546/inetaddress-getlocalhost-throws-unknownhostexception?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa . I'ts more that I'm just of the mindset of defensive programming for decreased maintenance. – searchengine27 Apr 23 '18 at 14:51