Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play! Framework FakeApplication - What does it actually do?

I've been looking at some posts on StackOverflow to see how to set up testing with a MYSQL database.

I simply want to test some controller methods which will need a test database with some data in to return meaningful results. I want to use a real MYSQL database as that is what I will be using in production and I have read that there are quite a few differences between MYSQL and the InMemory database provided by Play.

In posts such as this one and this blog about testing Play! applications, the posts show examples that initialise a FakeApplication object with database parameters and then call Helper.start(fakeApp).

The docs for Helper.start(FakeApplication app) gives the following description:

Starts a new application.

OK great. But what processes are actually triggered off by calling start and what will that give me in a test?

Map<String, String> settings = new HashMap<String, String>();
settings.put("db.default.url", "jdbc:mysql://localhost/testdb");
settings.put("db.default.user", "root");
settings.put("db.default.password", "");

app = Helpers.fakeApplication(settings);
Helpers.start(app);

I was hoping that the above code would configure Ebean to use my test database, but when I try to execute a method such as Ebean.save() I get an error saying that no default database has been registered with Ebean. In order to register I will need to populate a ServerConfig object and create an EbeanServer from the EbeanServerFactory. In that case was there any point in passing the settings map to the FakeApplication object? And again, just what does starting a FakeApplication actually do? How can it be used?

Thanks in advance.

like image 391
Joe Avatar asked Mar 22 '14 17:03

Joe


1 Answers

I realize that you asked this over a year ago, but it took me quite some time to figure this out for myself, so hopefully this helps someone at some point.

With a FakeApplication running, you can do basically everything you can after saying "activator run", except that there is not a webserver (netty) running serving up requests. You can though, test your controllers, as shown here, and call methods that access the database.

I actually referenced both of those posts you mention when figuring this out, in addition to this one, which discusses some special play magic config vars that must be set you'll see in my example below.

I created an abstract FakeApplicationTest class which I extend in the Unit/Functional tests. I first use my existing application.conf file, then overwrite certain parameters to test against a different database. Once the FakeApplication is started, I read in all the evolutions and create a fresh database to run my tests against.

public abstract class FakeApplicationTest {

    protected static FakeApplication app;

    /**
     * Create a new FakeApplication using all our custom config vars that test against diff DB's
     *
     * @return
     */
    public static FakeApplication createFakeApp() {
        // grab the main application.conf file that is used to start up the Play application
        Config config = ConfigFactory.parseFile(new File("conf/application.conf"));

        // resolve all variables within this config file with other variables within itself
        config = ConfigFactory.load(config);

        // create a Configuration object out of this and then turn it into a Map to be modified
        Configuration configuration = new Configuration(config);
        Map<String, Object> fakeApplicationConf = Maps.newHashMap(configuration.asMap());

        // do some crazy stuff here because Play wants us to for config voodoo, see:
        // http://www.stupidjavatricks.com/2013/05/changing-play-2-db-configuration-at-runtime-beware-of-dragons/
        Configuration akka = configuration.getConfig("akka.actor.serialization-bindings");
        addValue(fakeApplicationConf, "akka.actor.serialization-bindings", null);
        addValue(fakeApplicationConf, "akka.actor.serialization-bindings.\"[B\"", akka.getString("\"[\"B"));
        addValue(fakeApplicationConf, "akka.actor.serialization-bindings.\"java.io.Serializable\"", akka.getString("\"java.io.Serializable\""));

        // point at a different test database
        addValue(fakeApplicationConf, "db.default.url", "jdbc:mysql://127.0.0.1/testdb");
        addValue(fakeApplicationConf, "db.default.user", "someuser");
        addValue(fakeApplicationConf, "db.default.password", "hunter2");

        // disable evolutions and just create the DB manually
        addValue(fakeApplicationConf, "evolutionplugin", "disabled");

        return Helpers.fakeApplication(fakeApplicationConf);
    }

    @BeforeClass
    public static void setUp() throws Exception {
        app = createFakeApp();

        // fire up the Fake Application!
        Helpers.start(app);

        // after we start up the application, create a database
        createCleanDb();
    }

    @AfterClass
    public static void tearDown() throws Exception {
        Helpers.stop(app);
    }

    ...
}

There is quite a bit to reading out the evolutions and applying them in order too, but I've left that out for now.

like image 83
jcreason Avatar answered Nov 15 '22 05:11

jcreason