Observations on the Play! framework

Java stacks certainly are tall. You have your web server, your application server, servlet container, an IoC container, JPA, JAAS, JAX-RS, and that’s before you actually write any code.

The Play! framework seems set to change all that. It throws nearly all of the enterprise out of Java and instead provides you with a very structured, very Rails-like, web environment. You’ve got routes, controllers, something that resembles ActiveRecord, background jobs, built-in authentication, loads of nice plugins. In general, it’s very refreshing.

For example, in my (relatively flat) stack of Jersey and Jetty, it took me off-and-on about a week to implement Facebook authentication. Lots of fiddling with callback urls and hand-rolling Apache Shiro handlers. I got it working in the end, but it was pretty nasty. By comparison, using Play! was as simple as adding play -> fbconnect 0.5 to the dependencies.yml file (yes, that’s YAML in Java, not XML!) and changing my view to include a new #{fbconnect.button}. That’s it!

Play! also has a fairly unique feature in Java-land, and that’s dynamic reloading and compilation of classes. It’s just like using Ruby. Edit a file, refresh your browser and your changes are immediately visible; not just changes to views, but to the controllers and models too. A great improvement over the regular rebuild/reload cycle.

All in all, Play! has turned out to be an almost perfect Java web framework.

Almost.

Then we get to the testing story. I’m going to be blunt here. Despite Play! promoting how easy it is to test, I’m fairly sure the developers don’t actually do much testing; at the very least, they don’t do much unit testing.

Where to start?

Dependency injection

I’m not talking containers here. A fairly ubiquitous practice for testing web applications is to use constructor injection in your Controllers, injecting any services your controller needs into the constructor; those services are then used by the action methods to do their job, but more importantly they can be mocked or stubbed as part of a unit test.

An ASP.Net MVC example would look something like this:

public class MyController : Controller {
    readonly IMyService myService;

    public MyController(IMyService myService) {
        this.myService = myService;
    }

    public void Index() {
        myService.DoSomething();
    }
}

That way, in a unit test we can do this:

[Test]
public void should_do_something() {
    var myService = MockRepository.GenerateMock<IMyService>();

    new MyController(myService)
      .Index();

    myService.AssertWasCalled(x => x.DoSomething());
}

Piece of cake.

Play! on the other hand is not so simple. Play! requires controller action methods to be static; the justification for this is that controllers have no state, and thus are static. That makes sense, but it does so at the (in my opinion, fairly large) expense of constructor injection. You can’t call a static constructor, so you can’t pass in a dependency, so you can’t mock your dependency.

The equivalent example in Play! would be this:

public class MyController extends Controller {
    public static void index() {
        MyService myService = new MyServiceImpl();
        myService.doSomething();
    }
}

How can we test that controller in isolation? We can’t very easily. At least, not without using something like PowerMock (think TypeMock) to do some bytecode/reflection magic.

One proposed solution to this is to use an IoC container like Google Guice and inject a static field.

public class MyController extends Controller {
    @Inject
    MyService myService;

    public static void index() {
        myService.doSomething();
    }
}

That’s an improvement, but without constructor injection we have to bring a full container into the unit tests or make the field public and overwrite it manually. Not exactly pretty.

Another reason bandied around is “anaemic domain model”. Models should do things, I get that; however, we’re not in Ruby here, if my entity takes a hard-dependency on a service, how exactly am I supposed to test that in isolation? If an email should be sent when a user is created, I don’t want to have an actual SMTP server running just to execute my unit tests. In Ruby we could do some monkey patching and replace the SMTP service at runtime, but this is Java and we can’t do that (without resorting to service locators or singletons). I had an idea of using a JPA interceptor and injecting dependencies into an entity when it’s hydrated by Hibernate, but that just seems like a recipe for disaster.

So, deal breaker number 1: No easy way to mock dependencies, one way or another.

A brief diversion:
Play! doesn’t seem to really do unit testing. It refers to things as unit tests, but really they’re all integration tests. As mentioned already, you can’t easily replace your dependencies with stubs or mocks, so inevitably you need to run your tests against a real database, your emails to a real SMTP service, and your messages to a real messaging queue. This sucks.

I’m all for integration tests, and if I had to pick between them and unit tests, I’d put my money on integration tests; however, I’m not yet of the belief that I can live entirely without unit tests. Some things should still be tested in isolation; specifically, if I’m dealing with external services, I shouldn’t need them up-and-running to run a unit test.

IDE support

Java is where IDEs thrive. Whilst I know Play! is heavily influenced by Rails, I don’t yet think I could live without an IDE. IDEs have their strong points, and unit test runners are one of them. Great little things, one keyboard shortcut and all your tests are spinning away.

Not for Play! though, or not very easily anyway. Despite Play!s “unit tests” being based on JUnit, they can’t actually be ran as plain-old-JUnit tests. If you interact with any of the models, or any of the Play! specific classes, you need the full web environment to be available. In fact, the default runner for unit tests is the website itself. I’m all for running QUnit tests in the browser, but JUnit tests, really? No thanks.

Deal breaker number 2: Can’t run unit tests in the IDE.

It takes 6 seconds on my fairly meaty laptop to run one unit test. That’s unbelievable.

In addition, as Play! requires the web environment to run tests, that also means it kicks off any start-up jobs your application has. So whenever I run a test, it spins up my message queue, my database connection, and runs my data import routines (when in test mode).

Deal breaker number 3: Can’t run unit tests without spinning up the entire website (and that’s not fast).

Example projects

So there’s me thinking “It can’t possibly be this bad”. I decided to have a hunt around and see if there are any open-source applications built with Play!, or at the very least some reasonably sized examples. There were a few; however, none of them had test suites. In fact, nearly all of them still had the default tests that are provided with a new project.

public class BasicTest extends UnitTest {
    @Test
    public void aVeryImportantThingToTest() {
        assertEquals(2, 1 + 1);
    }
}

Finally, one thing that really made me feel that the developers don’t really get testing was their “mock” SMTP service. Take a look at line 36 of their Mail.java. A hand-rolled mock, in the main service. I don’t say this often but: WTF. Is this what’s considered good practice?

I’m so incredibly disappointed in Play!. It’s a wonderful framework which was obviously designed by people who don’t really do testing; or at the very least, don’t do anything other than end-to-end integration tests. I’d love to use Play!, but I just don’t know if I can get past these issues. Everything else about it has been such an improvement over my previous stack, but all of that is next to worthless if I can’t test it.

If anyone has any information or experiences to the contrary, I’d gladly be shown the err in my ways. How do you test with Play!? Integration tests only or is there some secret sauce I’m missing out on?

I really do want to like Play! but it just seems so difficult to do proper testing.

Some links:

Related Articles:

    Post Footer automatically generated by Add Post Footer Plugin for wordpress.

    This entry was posted in java and tagged , , . Bookmark the permalink. Follow any comments here with the RSS feed for this post.
    • Ryan Svihla

      It’s been awhile since I’ve been in the Java space but I seem to remember being able to run tests through Intellij when using the play “idealize” command see the following link

      http://www.playframework.org/documentation/1.2.3/ide 

      • Anonymous

        Hi Ryan, you are correct. I do actually do this, though re-reading what I wrote I can see how that’s not very clear. My real complaint is that whilst you can run them in your IDE, they require the full web stack to be available before they’ll execute; this basically means you get a minimum of a 5s overhead for running your tests. Five seconds doesn’t sound like much, I admit, but it quickly gets frustrating when you’re used to a fast cycle.

        Like many things in Play, I can live with it, and I can appreciate their decisions; however, it would be nice to not *have* to do it their way. I get the feeling Play! is at the place where Rails was a few years ago.

        Phase 1: “You WILL do it our way!”
        Phase 2: “Ok, you SHOULD do it our way but you can deviate a bit”
        Phase 3: “Our way is the default, do whatever the hell you want though.”

    • Royston Shufflebotham

      I’ve always subscribed to the notion that a framework API shouldn’t be considered as having ‘survived contact with the enemy’ until not only has somebody written something using your API, but that they have also written tests against their code.

      I’ve just started getting into Play, having previously used ASP.NET MVC and Rails, and having spent the last few years doing constructor-based injection testing, it’s  bit of a disappointment testing-wise. Didn’t they learn from the static unmockable HttpContent fiasco in ASP.NET? *sigh*

      But maybe it’s not a ‘deal breaker’ – can we make it work anyway?

      Controller action methods are meant to be extremely lightweight, and generally you want to get out of those and into services, repositories, other (non-REST) ‘controller’ objects as quickly as possible.  Those can all be developed using proper instances with constructor injection and all the usual good stuff, no?

      Play introduces its own ‘FunctionalTest’ and ‘UnitTest’ base classes, but you don’t have to use them: you can write your own (fast) plain JUnit tests as long as the code you’re testing doesn’t make heavy use of Play stuff, which is probably in the database level.  But then you’re straying out of unit tests and into more integration-y tests, perhaps?  The biggest area of irritation there is all the static fields inherited from Controller, e.g. ‘validation’.

      I dunno.  I like the feel of the framework (apart from the curious static methods everywhere) and so I _want_ it to work.  But when I see the great work that’s been going on in ASP.NET MVC, where they’ve made it really easy to use out-of-the-box yet have made everything completely pluggable and testable, Play seems a bit backward.

      I’m going to give it a try and see if I can bend it to my DI+unit testing will.  If not, I’m dumping it.  But what’s the alternative in Java land? :(

    • Samario

      Hello James,

      Five years ago you shared this knowledge <>, I don´t know how to implemented it, Can you please share a complete implementation, that is to say, a little visual studio project using the code.  I will be gratefull for your help. Thansk. Henry.-

      My email is : samario@gmail.com.

    • David Hesson

      Has this changed much at all in Play?  My suspicions after toying with Play 2.0 this morning were that testing was going to be annoying, especially isolation unit tests that I want to run fast, without services running.  Seems like this post confirms my fears.  I also see plenty of stack overflow posts mentioning that DI is unnecessary in Play  but I think people forget that well laid out services/controllers rely on interfaces and class composition to perform work, which invites DI and makes it easier to unit test this code as well as configure your application.

    • Ashish

      I am fairly new to Play, but seems very difficult to do DI. I am not able to get the confidence in doing TDD in Play