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.
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.
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?
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With