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.
Related
Class to be tested
public class KnockoutValidation {
public boolean runFormValidation(String param1, boolean param1, final String param3) {
//...
AccessBeanFactory fact = new AccessBeanFactory();
AccessBean bean = (AccessBean) fact.getAccessBean("abc", "xyz");
//....
}
}
Test Class
#RunWith(MockitoJUnitRunner.class)
public class KnockOutValidationTest {
#InjectMocks
KnockoutValidation KnockoutValidationMock;
#Mock
AccessBeanFactory factMock;
AccessBean accessBean;
#Before
public void setUp() {
accessBean = new AccessBean();
when(factMock.getAccessBean(anyString(), anyString())).thenReturn(accessBean);
}
#Test
public void doKnockoutValidationTest() {
Boolean result = KnockoutValidationMock.runFormValidation("a", true, "c");
Assert.assertEquals(result, true);
}
}
Even after mocking it is calling the actual implementation and throwing an exception and getting
java.lang.NullPointerException
ideally when we mock it should not execute actual method, here it is going into that getAccessBean method which is again a big API with a lot of try and catch blocks. So somewhere inside it is throwing an exception.
I just want to know why mocking is not working and how to mock this type of casted methods
I believe the way you had written implementation, it won't be possible reason is
AccessBeanFactory fact= new AccessBeanFactory();
instead you can
#Autowired private AccessBeanFactory fact;
Problem :- Every-time you call fact.getAccessBean with newly created object(instead of mock) while beans are not available. So it does throw NPE as expected
The #InjectMock won't work in this case because you are creating the AccessBeanFactory in place with a new constructor.
AccessBeanFactory fact= new AccessBeanFactory();;
You should have it as a field of the class, then the InjectMock will work, or better pass the factory as an argument.
Here is one example that should work. #InjectMock works by type, meaning that it will search through the class's field with Reflection and injects the mocks you specify with the #Mock annotation.
public class KnockoutValidation {
#Autowired
AccessBeanFactory fact;
public boolean runFormValidation(String param1, boolean param1, final String param3) {
//...
AccessBean bean = (AccessBean) fact.getAccessBean("abc", "xyz");
//....
}
}
You could also try to use PowerMockito's whenNew that will actually apply to the inline class creation, but that's a dark path you should avoid and only use with 3rd party codes.
I'm learning about dependecy injection and testing with Mockito. And I just found a tutorial where someone explain an application with dependency injection and without dependency injection. This is the link: https://www.journaldev.com/2394/java-dependency-injection-design-pattern-example-tutorial
I have 2 questions:
The first question is about the code that he writes for testing. What kind of mock is that? Don't you need to use #Mock to mock an object?
This is his code for testing:
public class MyDIApplicationJUnitTest {
private MessageServiceInjector injector;
#Before
public void setUp(){
//mock the injector with anonymous class
injector = new MessageServiceInjector() {
#Override
public Consumer getConsumer() {
//mock the message service
return new MyDIApplication(new MessageService() {
#Override
public void sendMessage(String msg, String rec) {
System.out.println("Mock Message Service implementation");
}
});
}
};
}
#Test
public void test() {
Consumer consumer = injector.getConsumer();
consumer.processMessages("Hi Pankaj", "pankaj#abc.com");
}
#After
public void tear(){
injector = null;
}
}
And the second question is about testing the app without dependency injection. I don't understand why he say that: "Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes." Why we cannot mock these objects in our test cases.
The first question is about the code that he writes for testing. What kind of mock is that? Don't you need to use #Mock to mock an object?
To mock an object you have to provide an object that has same type i.e behaves the same or is a subtype of the class that object you want to mock. So to mock an object you can use an instance of anonymous class (in your case it would be an object of anaonymous class that extends MyDIApplication) or you can use Mockito with its #Mock annotation which does basically similiar thing. Bascially using #Mock annotation is similiar to doing :
MyDIApplication myDiApplication = Mockito.mock(MyDIApplication.class)
which creates a mock object extending class passed in constructor. Here you may pass interface or class depending on what you want to mock.
When using anonymous class you provide implementation of methods that you want to mock in overriden implementations of methods, but in case of Mockito you provide intender behaviour by using methods like Mockito::when.
And the second question is about testing the app without dependency injection. I don't understand why he say that: "Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes." Why we cannot mock these objects in our test cases.
I guess you are refering to this piece of code :
public class MyApplication {
private EmailService email = new EmailService();
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.email.sendEmail(msg, rec);
}
}
Here you create an instance of EmailService as class field. So there is no possibilty you can mock this (although you could use reflection or PowerMock). So you are tightly coupled to EmailService and it is hard to test MyApplication class logic. To be able to test it you can use constructor injection :
public class MyApplication {
private EmailService email;
public MyApplication(EmailService emaliService) {
this.email = emailService;
}
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.email.sendEmail(msg, rec);
}
}
Or setter injection :
public class MyApplication {
private EmailService email;
public void setEmailService(EmailService emailService) {
this.email = emailService;
}
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.email.sendEmail(msg, rec);
}
}
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.
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 legacy class that contains a new() call to instantiate a LoginContext object:
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
}
}
I want to test this class using Mockito to mock the LoginContext as it requires that the JAAS security stuff be set up before instantiating, but I'm not sure how to do that without changing the login() method to externalize the LoginContext.
Is it possible using Mockito to mock the LoginContext class?
For the future I would recommend Eran Harel's answer (refactoring moving new to factory that can be mocked). But if you don't want to change the original source code, use very handy and unique feature: spies. From the documentation:
You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).
Real spies should be used carefully and occasionally, for example when dealing with legacy code.
In your case you should write:
TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);
I am all for Eran Harel's solution and in cases where it isn't possible, Tomasz Nurkiewicz's suggestion for spying is excellent. However, it's worth noting that there are situations where neither would apply. E.g. if the login method was a bit "beefier":
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
... and this was old code that could not be refactored to extract the initialization of a new LoginContext to its own method and apply one of the aforementioned solutions.
For completeness' sake, it's worth mentioning a third technique - using PowerMock to inject the mock object when the new operator is called. PowerMock isn't a silver bullet, though. It works by applying byte-code manipulation on the classes it mocks, which could be dodgy practice if the tested classes employ byte code manipulation or reflection and at least from my personal experience, has been known to introduce a performance hit to the test. Then again, if there are no other options, the only option must be the good option:
#RunWith(PowerMockRunner.class)
#PrepareForTest(TestedClass.class)
public class TestedClassTest {
#Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
TestedClass tc = new TestedClass();
tc.login ("something", "something else");
// test the login's logic
}
}
EDIT:
Modern versions of Mockito provide similar functionality without needing the extra PowerMock library with the mockito-inline dependency (instead of the mockito-core dependency):
public class TestedClassTest {
#Test
public void testLogin() {
try (MockedConstruction<LoginContext> mockedConstruction =
Mockito.mockConstruction(LoginContext.class)) {
TestedClass tc = new TestedClass();
tc.login("something", "something else");
// test the login's logic
}
}
}
You can use a factory to create the login context. Then you can mock the factory and return whatever you want for your test.
public class TestedClass {
private final LoginContextFactory loginContextFactory;
public TestedClass(final LoginContextFactory loginContextFactory) {
this.loginContextFactory = loginContextFactory;
}
public LoginContext login(String user, String password) {
LoginContext lc = loginContextFactory.createLoginContext();
}
}
public interface LoginContextFactory {
public LoginContext createLoginContext();
}
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
}
}
-- Test Class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(TestedClass.class)
public class TestedClassTest {
#Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
//comment: this is giving mock object ( lcMock )
TestedClass tc = new TestedClass();
tc.login ("something", "something else"); /// testing this method.
// test the login's logic
}
}
When calling the actual method tc.login ("something", "something else"); from the testLogin() {
- This LoginContext lc is set to null and throwing NPE while calling lc.doThis();
Not that I know of, but what about doing something like this when you create an instance of TestedClass that you want to test:
TestedClass toTest = new TestedClass() {
public LoginContext login(String user, String password) {
//return mocked LoginContext
}
};
Another option would be to use Mockito to create an instance of TestedClass and let the mocked instance return a LoginContext.
In situations where the class under test can be modified and when it's desirable to avoid byte code manipulation, to keep things fast or to minimise third party dependencies, here is my take on the use of a factory to extract the new operation.
public class TestedClass {
interface PojoFactory { Pojo getNewPojo(); }
private final PojoFactory factory;
/** For use in production - nothing needs to change. */
public TestedClass() {
this.factory = new PojoFactory() {
#Override
public Pojo getNewPojo() {
return new Pojo();
}
};
}
/** For use in testing - provide a pojo factory. */
public TestedClass(PojoFactory factory) {
this.factory = factory;
}
public void doSomething() {
Pojo pojo = this.factory.getNewPojo();
anythingCouldHappen(pojo);
}
}
With this in place, your testing, asserts and verify calls on the Pojo object are easy:
public void testSomething() {
Pojo testPojo = new Pojo();
TestedClass target = new TestedClass(new TestedClass.PojoFactory() {
#Override
public Pojo getNewPojo() {
return testPojo;
}
});
target.doSomething();
assertThat(testPojo.isLifeStillBeautiful(), is(true));
}
The only downside to this approach potentially arises if TestClass has multiple constructors which you'd have to duplicate with the extra parameter.
For SOLID reasons you'd probably want to put the PojoFactory interface onto the Pojo class instead, and the production factory as well.
public class Pojo {
interface PojoFactory { Pojo getNewPojo(); }
public static final PojoFactory productionFactory =
new PojoFactory() {
#Override
public Pojo getNewPojo() {
return new Pojo();
}
};
I happened to be in a particular situation where my usecase resembled the one of Mureinik but I ended-up using the solution of Tomasz Nurkiewicz.
Here is how:
class TestedClass extends AARRGGHH {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
Now, PowerMockRunner failed to initialize TestedClass because it extends AARRGGHH, which in turn does more contextual initialization... You see where this path was leading me: I would have needed to mock on several layers. Clearly a HUGE smell.
I found a nice hack with minimal refactoring of TestedClass: I created a small method
LoginContext initLoginContext(String login, CallbackHandler callbackHandler) {
new lc = new LoginContext(login, callbackHandler);
}
The scope of this method is necessarily package.
Then your test stub will look like:
LoginContext lcMock = mock(LoginContext.class)
TestedClass testClass = spy(new TestedClass(withAllNeededArgs))
doReturn(lcMock)
.when(testClass)
.initLoginContext("login", callbackHandler)
and the trick is done...