- HubPages»
- Technology»
- Computers & Software»
- Computer Science & Programming»
- Programming Languages
Inject a mock with JUnit
Inject that Mock!
I want to show you a trick to inject mock in a class when you can't (or just hate) to add get/set methods to the code that is to be tested.
If you are familiar with testing, in particular with Java/JUnit, you already know that to test a class in isolation, the base requirement is creating mocks of the classes that "collaborate" with the class you want to test, and inject the created mocks in it.
For example in this simple case:
package org.sample; public class Client { private Service simpleService; public Client() { simpleService = new Service(); } public String hello() { return simpleService.sayHello("Daniele"); } }
package org.sample; public class Service { public String sayHello(String name) { return "Hello " + name; } }
I want to test the class org.sample.Client mocking the simpleService instance of the class org.sample.Service. One of the things I can do to inject the mock of the Service class is adding a 'set' method to the Client class, like this:
public void setService (Service service) { this.simpleService = service; }
Sometimes you can't (it's happened to me working for some companies :)) or maybe you just don't want to add this kind of method to the application code. So, how can I test in isolation without it? In my test-tool-box I added this "utils" class:
package org.sample; import java.lang.reflect.Field; public class ReflectionUtils { public static Object getField(Object o, String fieldName) { Field field = getAccessibleField(fieldName, o.getClass()); Object fieldValue = null; try { fieldValue = field.get(o); } catch (IllegalAccessException e) { e.printStackTrace(); } return fieldValue; } public static void setField(Object o, String fieldName, Object inject) { Field field = getAccessibleField(fieldName, o.getClass()); try { field.set(o, inject); } catch (IllegalAccessException e) { e.printStackTrace(); } } // search in class, in case superclasses as well private static Field getAccessibleField(String fieldName, Class c) { Field field = null; while (c != null) { try { field = c.getDeclaredField(fieldName); field.setAccessible(true); break; } catch (NoSuchFieldException e) { c = c.getSuperclass(); } } return field; } }
Using it, we will get this test class:
package org.sample; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; public class ClientTest { private Client client; @Before public void setUpMyStuff() { client = new Client(); Service mock = new ServiceMock(); // in place of client.setService(mock); we have: ReflectionUtils.setField(client, "simpleService", mock); } @Test public void hello() { assertEquals("Goodbye Daniele", client.hello()); } // you can use something like Mockito to create a mock private class ServiceMock extends Service { @Override public String sayHello(String name) { return "Goodbye " + name; } } }
In this test the "Service" class is mocked and the mock replaces the message "Hello Daniele" with "Goodbye Daniele". To inject the mock I've used:
public static void setField(Object o, String fieldName, Object inject)
in the ReflectionUtils class. Respectively the 3 parameters passed in the method are:
- The instance of the class to test (or the class that needs a mock)
- The name of the property inside the class that represents the class to mock
- The mock instance
Try to run the test above un/commenting the line of code:
ReflectionUtils.setField(client, "simpleService", mock);
Enjoy!