Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing interactions with external services

prerequisites: I'm using the latest version of the Play! framework, and the Java version (not Scala).

I need to publish a message to a message queue when a user is created, and I'd like to test that behaviour. My issue is making this easily testable.

The Controller approach

In other frameworks, what I would've done would be to use constructor injection in the controller and pass in a mocked queue in my tests; however, with Play! the controllers are static, that means I can't do new MyController(mockedQueue) in my tests.

I could use Google Guice and put an @Inject annotation on a static field in my controller, but that doesn't feel very nice to me, as it either means I have to make the field public to be replaced in the test, or I have to use a container in my tests. I'd much prefer to use constructor injection, but Play! doesn't seem to facilitate that.

The Model approach

It's often said your logic should be in your model, not your controller. That makes sense; however, we're not in Ruby here and having your entities interact with external services (email, message queues etc...) is considerably less testable than in a dynamic environment where you could just replace your MessageQueue static calls with a mocked instance at will.

If I make my entity call off to the queue, how is that testable?

Of course, both these situations are unnecessary if I do end-to-end integration tests, but I'd rather not need a message queue or SMTP server spun up for my tests to run.

So my question is: How do I model my Play! controllers and/or models to facilitate testing interactions with external services?

like image 395
James Gregory Avatar asked Sep 18 '11 16:09

James Gregory


2 Answers

As I see, there's not a clean solution for this.

You could use an Abstract Factory for your dependencies. This factory could have setter methods for the objects it produces.

public class MyController {
    ...
    private static ServiceFactory serviceFactory = ServiceFactory.getInstance();
    ...
    public static void action() {
        ...
        QueueService queue = serviceFactory.getQueueService();
        ...
    }

}

Your test would look like this:

public void testAction() {
    QueueService mock = ...
    ...
    ServiceFactory serviceFactory = ServiceFactory.getInstance();
    serviceFactory.setQueueService(mock);
    ...
    MyController.action();
    verify(mock);
}

If you don't want to expose the setter methods of the factory, you could create an interface and configure the implementing class in your tests.

Another option would be the use o PowerMock for mocking static methods. I've used before and it works relatively fine for most cases. Just don't overuse it, or you're in maintenance hell...

And finally, since your willing to use Guice in your application, this could be a viable option.

Good luck!

like image 177
Andre Rodrigues Avatar answered Oct 22 '22 03:10

Andre Rodrigues


I'm little confused. You can call a method of another class

public class Users extends Controller {
    public static void save(@Valid User user) {
    //check for user validaton
    user = user.save();
    QueueService queueService = new QueueSerice();
    queueService.publishMessage(user);
    }
}

You can write unit testcases for QueueService using a mock and write Functional testcase for Users controller save method.

like image 40
basav Avatar answered Oct 22 '22 04:10

basav