How to unit-test in "tell, don't ask" follower classes? - java

I think the issue is explained best with an example.
public class MyService {
private OtherService theOther;
public void setTheOther(OtherService srv) { theOther = srv; }
public void myBusinessStuffFor(int id) {
theOther.applyToAllWith(id, new OtherService.Action() {
public void apply(Object whatever) {
doTheHardBusinessStuffWith(whatever);
}
}
}
private void doTheHardBusinessStuffWith(Object whatever) {
// here the business stuff provided by MyService
}
}
public interface OtherService {
void applyToAllWith(int id, Action action);
public interface Action {
void applyOn(Object whatever);
}
}
I like this pattern, because it's very cohesive. Action interfaces are paired with their Services. Business logic is not cluttered in many classes. Subclasses are only providing data to the action and don't have to be busy. I adopted it from here ( http://jamesladdcode.com/?p=12). The problem is that i didn't found a good solution for testing the behavior in the "doTheHardBusinessStuffWith(Object whatever)" method if i mock the otherService. With the mock i have to care how the business method gets called. But how can i do this. I use mockito and tried it with a ArgumentCapture already. But it don't feels right because of abusing ArgumentCapture.
I would like to know if the pattern used in class MyService.myBusinessStuffFor(int id) has a name (is it strategy pattern)?
But my major questions is how to make this code testable with mock of OtherService?

The other service is not really a business service in this case. Its only responsibility is to find the objects from the given ID, and apply the given action on these objects. Functionally, this is equivalent to the following code:
Set<Object> objects = otherService.getObjectsWithId(id);
for (Object o : objects) {
doTheHardBusinessStuffWith(o);
}
Make doTheHardBusinessStuffWith protected. Create a unit test for this method. This is the most important unit test: the one that tests the business logic.
If you really want to unit-test myBusinessStuffFor, what you could do is create a mock OtherService (and I mean implement this mock youself, here), that is built from a Set of objects, and applies its given action to all the objects in the set. Create a partial mock of MyService where the doTheHardBusinessStuffWith method is mocked, and which is injected with you mock OtherService. Call myBusinessStuffFor on the partial mock, and verify that doTheHardBusinessStuffWith has been called with every object of the set of objects.

You talk about mocking the OtherService. I don't know which mocking framework are you using; but you should be able to create a mock that just calls the applyOn method of the Action that gets passed to the applyToAllWith method, passing a mock object as the argument. In mockito, for example, this would be stubbed something like this.
doAnswer( new Answer<Object>(){
public Object answer( InvocationOnMock invocation ){
((Action) invocation.getArguments()[ 1 ]).applyOn( mockObject );
return null;
}}).when( mockOtherService ).applyToAllWith( anyInt(), any( Action.class ));
where mockOtherService is the mock you've created for the OtherService interface, and mockObject is whichever mock you want to pass to doTheBusinessHardStuffWith.

Related

How to spy on injected singleton?

I have a validation method in a model class that gets called from code that I don't have control over. This validation method has parameters that come from dependency injection. One of these parameters is a singleton (#Singleton) class.
#Singleton
public class ObjValidation {
#Inject
public ObjValidation() {
objsCache = new HashSet<>();
}
public boolean exists(String obj) {
return objsCache.contains(obj) || lookupObj(obj);
}
...
}
As you can see this singleton does perform a lookup on a database if the object isn't contained in the cache. While testing this database isn't there and should not be tested. Therefore I'm trying to mock the exists() method to return true without being run.
My first thought was, since this is a singleton, if I would inject the singleton into my test case before running it, I would create the singleton instance therefore could spy on it.
ObjValidation objValidation = spy(app.injector().instanceOf(ObjValidation.class));
doReturn(true).when(objValidation).exists(any());
But yes, spy only returns a copy and therefore doesn't spy on the instantiated singleton. I would need to replace the singleton instance with the spy copy to make it work this way.
How can I do this?
Further explanation of the test case:
In my test (method under test is register()) I bind a scala.html view form to a model class via the build in functionality of the play framework.
public Result register() throws ResultMessageException {
...
Form<Registration> filledRegistrationForm = this.formFactory.form(Registration.class).bindFromRequest();
...
}
The model has a validation method that has parameters that get injected as described in the play documentation: https://www.playframework.com/documentation/2.6.x/JavaForms#Custom-class-level-constraints-with-DI-support
#ValidateWithDI
public class Registration implements ValidatableWithDI<ValidationError> {
#Required
private String obj;
#Override
public ValidationError validate(ObjValidation objValidation) {
if (!objValidation.exists(obj)) {
return new ValidationError("obj");
}
return null;
}
}
This validation method gets called by the framework while binding the form.

How can we test that a class implements many interfaces?

My question is about testing a class that implements many interfaces. For example, I have this class:
public class ServiceControllerImpl extends ServiceController implements IDataChanged, IEventChanged {
}
Now there are two ways for testing. The first is testing directly on the concrete class. That means the object type is the concrete class rather than the interface.
public class ServiceControllerImplTest {
ServiceControllerImpl instance;
#Before
public void setUp() {
instance = new ServiceControllerImpl();
// you can bring this instance anywhere
}
}
The second way is testing on the interface only. We must typecast this object to all interfaces it implements.
public class ServiceControllerImplTest {
ServiceController instance; // use interface here
IDataChanged dataChangeListener;
#Before
public void setUp() {
instance = new ServiceControllerImpl();
dataChangeListener = (IDataChanged) instance;
// instance and dataChangeListener "look like" two different object.
}
}
I prefer the second solution because maybe in future we can change the interface it implements to other objects, so using the concrete class might lead to failing tests in the future. I don't know the best practice for this problem.
Thanks :)
I prefer second solution because in reality, maybe in future we can change the interface it implements to other objects, so force using concreted class maybe leads to fail test in the future.
I guess it will lead to failed tests anyway, because you usually test that assertions are true or false. The question is: Do that tests apply to any IDataChanged or do these assertions only apply to the ServiceControllerImpl?
If the assertions only apply to the ServiceControllerImpl it doesn't matter if you use an IDataChanged instead of an ServiceControllerImpl, because you must edit the test when you use another IDataChanged object - different assertions. The test will fail if you use another object.
The way you setup unit tests Itself gives you an answer. A unit test usually tests one class in isolation. This means that you mock the environment. But mocking the environment means that you know the dependencies of the class you test and this are implementation details. So your test is written on an implemtation basis rather than only the interface.
It's possible to write tests that only test an abstract api - like an interface. But this usually means that your tests are abstract too. E.g.
public abstract class SetTest {
#Test
public void addAlreadyExistentObject(){
Set<String> setUnderTest = createSetUnderTest();
Assert.assertTrue(setUnderTest.isEmpty());
boolean setChanged = setUnderTest.add("Hello");
Assert.assertTrue(setChanged);
setChanged = setUnderTest.add("Hello");
Assert.assertFalse(setChanged);
Assert.assertEquals(setUnderTest.size(), 1);
}
protected abstract Set<String> createSetUnderTest();
}
You can then extend these abstract tests to test the api for concrete classes. E.g.
public class HashSetTest extends SetTest {
#Override
protected Set<String> createSetUnderTest() {
return new HashSet<String>();
}
}
In this case you can replace the implementation and the test must remain green.
But here is another example of an abstract api when replacing the object under test does not really make sense.
What about writing a test for all Runnables?
public class RunnableTest {
#Test
public void run(){
Runnable runnable = ...;
// What to test here?
// run is invoked without throwing any runtime exceptions?
runnable.run();
}
}
As you can see it does not make sense in some cases to write tests in a way so that you can easily replace the object under test.
If an api like the Set api defines a concrete state handling you can write abstract tests that test this.
JayC667 already correctly answered that it's best to refer to a class through its supertype(s) in tests of methods defined by those types. But I'd change the way you did that a bit to avoid casting:
public class ServiceControllerImplTest {
ServiceController controller;
IDataChanged dataChangeListener;
#Before
public void setUp() {
instance = new ServiceControllerImpl();
controller = instance;
dataChangeListener = instance;
}
}

Mockito and interface event

I am writing an integration test using mockito.
The unit under test is connected to a mocked object (objA) through an interface. The functionality that I am trying to mimic happens when the mocked objected fires an event and the unit under test is listening to it.
The interface:
public interface MyInterfaceAPI{
void fireyMyEvent(String msg);
}
The unit under test:
public class UnitUnderTest{
ObjA objA;
public UnitUnderTest(ObjA objA_t) {
objA = objA_t;
objA.addMyListener(new addMyHandler());
}
class addMyHandler implements MyInterfaceAPI{
#Override
public void fireyMyEvent(String msg) {
System.out.println(msg);
};
};
};
The test:
public class MyTest {
#org.junit.Test
public void run() {
ObjA mockObjA = mock(ObjA .class);
UnitUnderTest spyController = Mockito.spy(new UnitUnderTest());
MyInterfaceAPI mo2uut= mock(MyInterfaceAPI.class);
mo2uut.fireyMyEvent("hello from test");
}
}
My question is in the test, how do I connect the mo2uut ('mocked object' to 'unit under test') to the addMyHandler class implementation of MyInterfaceAPI inside the UnitUnderTest?
I am clearly missing something, but I am not sure what.
You have 2 schools on unit testing: The London / Mockist school, and the Detroit school.
If you want to use mocks, you must use dependency injection, so you can replace the dependencies with mocks. I think most people following the Detroit school would agree on this too, just because using dependency injection "is a good thing" (tm).
What you can do is to pass an instance of ObjA to UnitUnderTest in the constructor; Or alternatively (if ObjA is a collection) add the method UnitUnderTest.addListener(), where you pass an instance of a handler. With these 2 approaches, you'll be injecting a handler.
About using powermock: Powermock is a beast better used on old projects that have very little unit testing and their dependencies are a mess. If you are coding this now, using power mock is wrong (in the spirit of fairness, this is a biased idea, but it's shared with many other people).
Edit
Now I get your question! And I think that you're trying to test too much in one unit test and that causes the problem. Again, the mockist school talks about testing interactions... that's the key point. So in the test for UnitUnderTest the only interaction is with ObjA to set the handler, and that's the end of the story.
You'll probably have another test for ObjA to ensure that all handlers are invoked.
Now the last bit is how to test the code of the handler. But before that, please appreciate how independent each test is, as you're testing the interactions (and any logic in the code), but not more than 1 thing.
About the handler... you might not like this, but you have to make that class accessible, either make it public or extract it to another public class. If you extract it, you can put in an internal package so it's clear that the class shouldn't be used by anyone else.
If you have an hour to spare, I would suggest you to watch this great presentation: The Deep Synergy Between Testability and Good Design by Michael Feathers where he goes into a similar example of what you have in your code and why it makes sense to separate it.
Use PowerMock's PowerMockito to intercept the call to the addMyHandler class injecting a mock of MyInterfaceAPI as explained in Ben Kiefer's tutorial on "PowerMockito: Constructor Mocking"
I have managed to make it working. Posting here the fixed code for people who see this in the future.
public interface MyInterfaceAPI{
void fireyMyEvent(String msg);
}
The unit under test:
public class UnitUnderTest{
private ObjA objA;
public MyInterfaceAPI interfaceHandler;
public UnitUnderTest(ObjA objA_t) {
objA = objA_t;
interfaceHandler = new addMyHandler();
objA.addMyListener(interfaceHandler);
}
class addMyHandler implements MyInterfaceAPI{
#Override
public void fireyMyEvent(String msg) {
System.out.println(msg);
};
};
};
The test:
public class MyTest {
#org.junit.Test
public void run() {
ObjA mockObjA = mock(ObjA .class);
UnitUnderTest spyController = Mockito.spy(new UnitUnderTest());
spyController.hnd.fireyMyEvent("hello from test");
}
}

Mockito NotaMockException

I am facing an issue with Mockito junit testing. I am new to it and am a bit confused with the problem I am facing. Any help on this would be appreciated.
class Activity{
public void firstMethod(){
String str = secondMethod();
}
public String secondMethod(){
String str = null;
/* some Code */
return str;
}
}
Getting exception :
*org.mockito.exceptions.misusing.NotAMockException:
Argument passed to when() is not a mock!*
in the below code
class ActivityTest(){
Activity act;
#Before
public void setup(){
act = new Activity();
}
#Test
public void testFirstMethod(){
Mockito.doReturn(Mockito.anyString()).when(act).secondMethod();
act.firstMethod();
verify(act).secondMethod();
}
}
I am aware that activity is not a mock but I am not sure for a way around this as secondMethod() is a method in the same class. I need to write rule for secondMethod() as I have already done its Unit Testing. The definition of secondMethod() consists has external dependencies. Should I be mocking the external dependencies present in secondMethod() and writing rules for them rather than rule for secondMethod()?
I found this post:
Mockito Spy'ing on the object being unit tested
However separating the secondMethod() into a different class does not make sense. My method is related to this class. Creating a different class for testing does not seem right to me. Even mocking the actual class using spy() is not the most correct way as already explained in the post.
I don't think I should be creating a mock of the Activity class as that is the class I am testing. I would really appreciate help and insights into this.
As you noted, act is not a mock, and therefore you cannot record behavior on it. You could use Mockito.spy to, well, spy (or partially mock) the act object so that you only record the behavior of secondMethod and execute the actual code for firstMethod.
Note, however, that matchers can't be used in doReturn calls regardles of how you're mocking or spying your object. A return value must be a concrete object.
class ActivityTest() {
Activity act;
#Before
public void setup(){
act = Mockito.spy(new Activity()); // Here!
}
#Test
public void testFirstMethod(){
Mockito.doReturn("someString").when(act).secondMethod();
act.firstMethod();
verify(act).secondMethod();
}
}
A slightly more elegant syntax allows you to use annotations instead of explicitly calling Mockito.spy, but it's a matter of taste really:
#RunWith(MockitoJUnitRunner.class)
class ActivityTest() {
#Spy
Activity act = new Activity();
#Test
public void testFirstMethod(){
Mockito.doReturn("someString").when(act).secondMethod();
act.firstMethod();
verify(act).secondMethod();
}
}
There is no reason to mock anything in this example. Since there are no dependencies and both methods are public, you can test them directly.
public class ActivityTest() {
private Activity act = new Activity();
#Test
public void testSecondMethod(){
assertEquals("expected-value", act.secondMethod());
}
#Test
public void testFirstMethod() {
act.firstMethod();
// success if no exception occurs
}
}
Since firstMethod does not have any detectable effect on the Act instance, nor on any dependency (since there are none) you can simply call the method and be satisfied if no exception is thrown. One could also reason that such a method should not be tested at all.
I assume the example given is a simplification of a class where calling firstMethod actually does have side effects, who knows...
Here are some hints:
Mock the Activity.
Tweak the behavior of secondMethod with when / then / doReturn
Use doCallRealMethod when firstMethod is invoked.
Hope it helps.

Mockito, Testing an object that relies on injected dependencies (Spring)?

I'm new to using Mockito and am trying to understand a way to make a unit test of a class that relies on injected dependencies. What I want to do is to create mock objects of the dependencies and make the class that I am testing use those instead of the regular injected dependencies that would be injected by Spring. I have been reading tutorials but am a bit confused on how to do this.
I have one the class I want to test like this:
package org.rd.server.beans;
import org.springframework.beans.factory.annotation.Autowired;
public class TestBean1 {
#Autowired
private SubBean1 subBean1;
private String helloString;
public String testReturn () {
subBean1.setSomething("its working");
String something = subBean1.getSomething();
helloString = "Hello...... " + something;
return helloString;
}
Then I have the class that I want to use as a mock object (rather than the regular SubBean1 class, like below:
package org.rd.server.beans.mock;
public class SubBean1Mock {
private String something;
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}
I just want to try running a simple test like this:
package test.rd.beans;
import org.rd.server.beans.TestBean1;
import junit.framework.*;
public class TestBean1Test extends TestCase
{
private TestBean1 testBean1;
public TestBean1Test(String name)
{
super(name);
}
public void setUp()
{
testBean1 = new TestBean1();
// Somehow inject the mock dependency SubBean1Mock ???
}
public void test1() {
assertEquals(testBean1.testReturn(),"working");
}
}
I figure there must be some fairly simple way to do this but I can't seem to understand the tutorials as I don't have the context yet to understand everything they are doing / explaining. If anyone could shed some light on this I would appreciate it.
If you're using Mockito you create mocks by calling Mockito's static mock method. You can then just pass in the mock to the class you're trying to test. Your setup method would look something like this:
testBean1 = new TestBean1();
SubBean1 subBeanMock = mock(SubBean1.class);
testBean1.setSubBean(subBeanMock);
You can then add the appropriate behavior to your mock objects for whatever you're trying to test with Mockito's static when method, for example:
when(subBeanMock.getSomething()).thenReturn("its working");
In Mockito you aren't really going to create new "mock" implementations, but rather you are going to mock out the methods on the interface of the injected dependency by telling Mockito what to return when the method is called.
I wrote a test of a Spring MVC Controller using Mockito and treated it just like any other java class. I was able to mock out the various other Spring beans I had and inject those using Spring's ReflectionTestUtils to pass in the Mockito based values. I wrote about it in my blog back in February. It has the full source for the test class and most of the source from the controller, so it's probably too long to put the contents here.
http://digitaljoel.nerd-herders.com/2011/02/05/mock-testing-spring-mvc-controller/
I stumbled on this thread while trying to set up some mocks for a slightly more complicated situation and figured I'd share my results for posterity.
My situation was similar in the fact that I needed to mock dependencies, but I also wanted to mock some of the methods on the class I was testing. This was the solution:
#MockBean
DependentService mockDependentService
ControllerToTest controllerToTest
#BeforeEach
public void setup() {
mockDependentService = mock(DependentService.class);
controllerToTest = mock(ControllerToTest.class);
ReflectionTestUtils.setField(controllerToTest, "dependantService", mockDependentService);
}
#Test
void test() {
//set up test and other mocks
//be sure to implement the below code that will call the real method that you are wanting to test
when(controllerToTest.methodToTest()).thenCallRealMethod();
//assertions
}
Note that "dependantService" needs to match whatever you have named the instance of the service on your controller. If that doesn't match the reflection will not find it and inject the mock for you.
This approach allows all the methods on the controller to be mocked by default, then you can specifically call out which method you want to use the real one. Then use the reflection to set any dependencies needed with the respective mock objects.
Hope this helps someone down the road as it stumped me for a while.

Categories

Resources