3

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?

Per Newgro
  • 93
  • 2
  • 11

2 Answers2

1

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.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Making doTheHardBusinessStuffWith protected is not an option so far. It's private behavior and should stay private. myBusinessStuffFor is the entry point for this class, so it should be unit tested. The object collecting behavior is exactly that what i want to hide. You have to run the loops always twice - at collecting the raw objects and then to apply the business logic. And it's only known by the OtherService that it can handle multiple objects. And implementing OtherService as an adapter to call the action could become hard in other usecases. – Per Newgro Jan 20 '12 at 09:12
  • Then test it as part of the test for `myBusinessStuffFor`. But you're making your life difficult. – JB Nizet Jan 20 '12 at 09:17
-1

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.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
  • This is exactly what i was looking for. If only someone could tell me how this pattern is named - then i would be satisfied completely. – Per Newgro Jan 20 '12 at 09:18
  • Sorry, I'm not very good with knowing the names for stuff. It just seems to me to be the common-sense way of testing your code; I'm not aware of any name for this particular pattern. – Dawood ibn Kareem Jan 20 '12 at 10:30
  • Well, yes, but I feel that the "command pattern" is something more general than this. This is just an application of the "command pattern" to unit testing, but I can't help thinking that the OP was after a more specific name. Maybe not. – Dawood ibn Kareem Jan 20 '12 at 10:50
  • lol. Why do i always think diagonally :-). Command pattern seems to describe my business method behavior really. I always thought about something like "behavior injection" or so. Thx you helped me both alot. – Per Newgro Jan 20 '12 at 14:30