Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top