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();
}
}
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
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.
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.
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>
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";