I've written a class which reads the entire file and returns the content.
class ClassToTest {
public methodToTest(String input) {
return privateMethod(input);
}
private privateMethod(input) {
ClassPathResource classPathResource = new ClassPathResource(input);
IOUtils.toString(classPathResource.getFile());
}
}
Now, inside my test class, I don't want my test to actually read the file from so I'm trying to mock the method classPathResource.getFile() but somehow I'm not able to do so without writing PrepareForTests() and if I do that those test are not counted in JaCoCo.
I've written test case as
#Test
public void test_methodToTest() {
mockStatic(IOUtils.class);
when(IOUtils.toString(any()).thenReturn("DUMMY_STRING");
methodToTesT("file1.txt");
...
}
The problem is IOUtils.toString gets mocked properly but the call classPathResource.getFile() tries to access the file on the disk. For this, I can do this
PowerMockito.whenNew(ClassPathResource.class)
.withAnyArguments().thenReturn(mockedClassPath);
And add annotation to my test class as
#PrepareForTest(ClassToTest.class)
class MyTestClass {
...
}
But now the problem is this test class is skipped from the JACOCO test coverage . How can I write tests for this class?
You can pass a mocked reference into the constructor doing this:
class ClassToTest {
private ClassPathResource classPathResource;
public ClassToTest(ClassPathResource classPathResource) {
this.classPathResource = classPathResource;
}
public methodToTest(String input) {
IOUtils.toString(classPathResource.getFile(input));
}
}
Or you can pass the mocked reference into the method doing this:
class ClassToTest {
public methodToTest(ClassPathResource classPathResource) {
IOUtils.toString(classPathResource.getFile());
}
}
Having to mock a private member should be seen as a code smell and an indication that something is wrong with the current design. Because ClassPathResource is being initialized internal to the subject class it is now tightly coupled to that class. While not entirely impossible to mock it does make testing the class cleanly more difficult. Consider inverting the creation of the class to a delegate as a dependency.
public interface PathResource {
String getFile(String input);
}
This will allow the injection of the dependency
class ClassToTest {
private classPathResource;
public ClassToTest (PathResource resource) {
this.classPathResource = resource;
}
public String methodToTest(String input) {
return privateMethod(input);
}
private String privateMethod(String input) {
return IOUtils.toString(classPathResource.getFile(input));
}
}
and the dependency can be mocked/faked/stubbed when testing.
public void Test() {
//Arrange
//mock creation
PathResource resource = mock(PathResource.class);
String input = "path";
String expected = "expected_output";
//stubbing
when(resource.getFile(input)).thenReturn(expected);
ClassToTest subject = new ClassToTest(resource);
//Act
String actual = subject.methodToTest(input);
//Assert
verify(resource).getFile(input);
assertEquals(expected, actual);
}
in production code the ClassPathResource would be derived from the abstraction
public class ClassPathResource implements PathResource {
//...code removed for brevity
}
and it would be associated with the abstraction at the composition root.
Following the above suggestions would now allow ClassToTest to be tested in isolation without any knock on effects of implementation concerns.
Related
Is it possible to test that the "innerMethod" was called without modifying the Class class?
I need to make a unit test in a separate class both scenario of the "someCondition".
The problem is that the method is void so I cannot make use of the return type. The only way would be to check if the "innerMethod" was called.
I was thinking to use Mokito verify but this method is called inside a method on an object created at runtime.
Any suggestion is most welcome.
public class Class {
public void outerMethod(outerObj) {
if(someCondition) {
Object innerObj = new Object();
innerObj.innerMethod(outerObj);
} else {
//other code
}
}
You can achieve that with the use of Mockito::times and Mockito::verify methods.
test setup would be as follows:
#InjectMocks
private SomeService service;
#Mock
private SomeHelper helper;
and then test that some method from the helper has been involved in the following manner:
#Test
public void testInnerHasBeenCalledOnce() throws Exception {
service.outherMethodName(someParam);
Mockito.verify(helper, Mockito.times(1)).innerMethodName(someParamSecond);
}
I am trying to create a test for a helper class but I am unable to mock a static call inside the helper class.
This is my class to be tested:
public class NoteHelper {
private NoteService noteService = ServiceBuilder.getService(NoteService.class);
public NoteResponse createNewNote(NoteRequest noteRequest) throws NotAuthorizedException {
Note note = new Note();
note.setContent(noteRequest.getContent());
noteService.addNote(note); // throws NotAuthorizedException
NoteResponse noteResponse = new NoteResponse();
noteResponse.setContent(note.getContent());
return noteResponse;
}
}
Here is my test class:
#RunWith(PowerMockRunner.class)
public class NoteServiceHelperTest {
String dummyContent = "ABCD";
#InjectMocks
private NoteHelper noteHelper;
#Mock
private NoteService noteService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
#PrepareForTest({NoteHelper.class, ServiceBuilder.class})
public void createNewNoteTest() throws NotAuthorizedException {
noteService = Mockito.mock(NoteService.class);
PowerMockito.mockStatic(ServiceBuilder.class);
PowerMockito.when(ServiceBuilder.getService(NoteService.class))
.thenReturn(noteService);
doNothing().when(noteService).addNote(any(Note.class));
NoteRequest request = new NoteRequest();
request.setContent(dummyContent);
NoteResponse response = noteHelper.createNewNote(request);
assertEquals(response.getContent(), dummyContent);
}
}
From what I read I thought that the call to ServiceBuilder.getService(...) will get replaced and noteService will be using a mocked instance instead of a real instance.
The problem is that this is not happening and the getService(...) method is actually called and eventually fails due to some external system dependencies (ServiceBuilder.getService(...) needs to acquire some database connections and make HTTP calls to other systems before it returns the NoteService instance).
So my question is how can I use power mockito to mock the getService(...) and the noteService.addNote(note) calls ?
I have the following method
public static File getInventoryFileFromProperties(){
String filePath = getProperty(ConfigProperties.MY_INVENTORY_FILE);
logger.debug("Looking for inventory file at {}", filePath);
return new File(filePath);
}
How do i unit test this for the following condition, ConfigProperties.MY_INVENTORY_FILE is not present in the properties file.
getProperty() // gets values from property file
ConfigProperties.MY_INVENTORY_FILE // is an enum of keys
The best way to make code that accesses external resources -- such as the file system -- unit testable, is by creating an abstraction layer, e.g.:
public class FileAccessor {
public String getProperty(ConfigProperties property) {
// property file access code goes here
}
public File createFile(String filePath) {
return new File(filePath);
}
}
Then, the class-under-test can be refactored to use the resource-accessor through constructor injection of the dependency:
public class ContainingClass {
private FileAccessor fileAccessor;
// this constructor is accessible for the unit tests
ContainingClass(FileAccessor fileAccessor) {
this.fileAccessor = fileAccessor;
}
// this constructor is used by normal client code
public ContainingClass() {
this(new FileAccessor());
}
public File getInventoryFileFromProperties(){
String filePath = fileAccessor.getProperty(ConfigProperties.MY_INVENTORY_FILE);
return fileAccessor.createFile(filePath);
}
}
Finally, unit testing becomes simpler now that you can mock the file access. This test uses the Mockito mocking framework for mocking the dependency, and also works with earlier versions of JUnit:
import static org.mockito.Mockito.*;
import org.junit.Test;
public class ContainingClassTest {
#Test
public void getInventoryFileFromProperties_MY_INVENTORY_FILE_isMissing() {
FileAccessor fileAccessor = mock(FileAccessor.class);
// arrange the config file to return a null value for the property
when(fileAccessor.getProperty(ConfigProperties.MY_INVENTORY_FILE)).thenReturn(null);
// act; call the method
new ContainingClass(fileAccessor).getInventoryFileFromProperties();
// assert that the file-creating method was called with a null path
verify(fileAccessor).createFile(isNull(String.class));
}
}
I have an application with 3 layers:
App <--> Graph <--> Couchbase
I'm trying to test the GraphConnector by mocking the couchbase layer and "replacing" it with a very basic in-memory graph implementation, using the same approach demonstrated in the JMockit tutorial.
This is my test class (pardon the poor indentation, didn't get the hang of it yet):
public class GraphConnectorTest {
public static final class MockCouchbase extends MockUp<ICouchConnector> {
private Map<String, CouchEntry> couch;
#Mock
public void $clinit() {
couch = new HashMap<String, CouchEntry>();
}
#Mock
public void put(CouchEntry entry) {
couch.put(entry.getKey(), entry);
}
#Mock
public CouchEntry get(String key) {
return couch.get(key);
}
}
GraphConnectorImpl graph = new GraphConnectorImpl();
#BeforeClass
public static void setUpClass() {
new MockCouchbase();
}
#Test
public void testPost() throws Exception {
GraphNode node = new GraphNode(GraphNodeType.DOMAIN, "alon.com");
graph.post(node);
GraphNode retNode = graph.getSingleNode(node.getValue(), node.getType());
assertEquals(node.getValue(), retNode.getValue());
assertEquals(node.getType(), retNode.getType());
}
}
And here is my class under test:
public class GraphConnectorImpl implements IGraphConnector {
private static ICouchConnector couch = new CouchConnectorImpl(); // <-- Basic implementation which I don't want the test to execute
#Override
public void post(GraphNode node) {
CouchEntry entry = new CouchEntry(node.getValue(), JsonDocument.create(node.getValue()));
couch.put(entry);
}
#Override
public GraphNode getSingleNode(String nodeName, GraphNodeType nodeType) {
return new GraphNode(nodeType, couch.get(nodeName).getKey());
}
}
For some reason, the class MockCouchbase that I created within the test class isn't automatically bound to the private field ICouchConnector couch of the tested class, as shown in the tutorial. Instead, the real implementation is called, which is obviously undesirable.
If I remove the reference to the real implementation, I just get a good ol' NullPointerException.
I tried playing with the #Tested and #Injectable annotations but to no avail.
Solving my own question.
The problem with the way I wrote the class under test was explicitly invoking the constructor of the real implementation. I'll be surprised if any mocking framework can "bypass" that.
Instead, I should've created a constructor that gets ICouchConnector as one of its arguments, e.g. use dependency injection properly.
public class GraphConnectorImpl implements IGraphConnector {
private static ICouchConnector couch;
public GraphConnectorImpl(ICouchConnector connector) {
couch = connector;
}
// Rest of class...
}
JMockit will then attempt to find a constructor that corresponds to the fields annotated #Tested and #Injectable in the test class.
public class GraphConnectorTest {
#Tested
GraphConnectorImpl graph;
#Injectable
ICouchConnector couch;
// Rest of class...
}
I have a Java class that simply extends a library class and calls a method of its parent with a default parameter. How do I write a Junit test for that? A MockObjectTestCase is good too. Here is an example of what I'm talking about:
public class ResourceBundleMessageSource {
public String getMessage(String key, Object[] objects, Locale locale) {
//Spring library method
}
}
public class MessageResource extends ResourceBundleMessageSource {
public String getMessage(String key) {
return (getMessage(key, null, Locale.getDefault());
}
}
I know the wrapper method isn't even necessary, but makes frequent calls to it easier. Note the class works fine, I'm only interested in how the unit test is written.
If you would be willing to refactor your class slightly, I would recommend MessageResource delegate to a MessageSource instance, rather than extend ResourceBundleMessageSource. Then I'd use mocks in my unit test. Something like this:
public class MessageResource implements MessageSource {
private final MessageSource delegate;
public MessageResource(MessageSource delegate) {
this.delegate = delegate;
}
public String getMessage(String key) {
return delegate.getMessage(key, null, Locale.getDefault());
}
// need to implement three other MessageSource methods,
// simple pass-throughs to delegate
}
and unit test
public class MessageResourceTest {
private MessageSource mockDelegate;
private MessageResource messageResource;
#Before
public void setUp() throws Exception {
mockDelegate = //mock with your favorite framework, or by hand
messageResource = new MessageResource(mockDelegate);
}
#Test
public void testGetMessage() {
String key = "foo";
String actualMessage = messageResource.getMessage(key);
assertEquals(key, /* get key passed to mock delegate */ );
assertSame(Locale.getDefault(), /* get Locale passed to mock delegate */);
assertEquals(/*expected message from mock*/, actualMessage);
}
}
For this particular example I probalby would not bother to test it.
If you do need to test it, try something like:
#Test
public void getDefaultMessage() {
ResourceBundleMessageSource origSource = <create source>
MessageResource subSource = <create with same criteria as origSource>
String key = <some key that is locale-specific>
assertEquals(origSource.getMessage(key, null, Locale.getDefault()),
subSource.getMessage(key));
}
If the first two lines are hard to write, then it makes even more sense not to test it.
If you have several tests like this, move the first two lines into a setup fixture.
I don't think it's even worth writing a unit test for that. If there's already a test for ResourceBundleMessageSource.getMessage(), then that should be good enough.