Play 2.0 FakeApplication setup with test configuration
Asked Answered
W

5

9

I have a specs2 test which uses a FakeApplication and an embedded mongodb database.

def inMemoryMongoDatabase(name: String = "default"): Map[String, String] = {
    val dbname: String = "play-test-" + scala.util.Random.nextInt
    Map(
        ("mongodb." + name + ".db" -> dbname),
        ("mongodb." + name + ".port" -> EmbeddedMongoTestPort.toString))
}

override def around[T <% Result](t: => T) = {
    running(FakeApplication(additionalConfiguration = inMemoryMongoDatabase(), additionalPlugins = Seq("se.radley.plugin.salat.SalatPlugin"))) {
        t // execute t inside a http session
    }
}

The FakeApplication uses the default application.conf configuration in the conf directory and additional configuration for the test databases that are created for each test.
This was working find until we setup a mongodb replicat set. Now the application.conf contains configuration for this replicat set

mongodb.default.replicaset {
host1.host = "localhost"
host1.port = 27017
host2.host = "localhost"
host2.port = 27018
host3.host = "localhost"
host3.port = 27019
}

As the FakeApplication uses the default configuration the tests fail because the hosts of the replicaset cannot be found. I want to have a different configuration for my tests, basically remove the mongodb.default.replicaset entry. If mongodb.default.replicaset were a simple Map[String, String] that would be easy as I could just add it to the additonalConfiguration but when I try to do that it fails because the expected value type is not a String but an Object. I have also tried to provide a separate test.conf file to the FakeApplication via the path parameter.

override def around[T <% Result](t: => T) = {
    running(FakeApplication(path = new java.io.File("conf/test.conf"), additionalConfiguration = inMemoryMongoDatabase(), additionalPlugins = Seq("se.radley.plugin.salat.SalatPlugin"))) {
        t // execute t inside a http session
    }
}

That didn't work either as it didn't load any config.

I would greatly appreciate any help. Thanks.

Chris

Wiper answered 3/9, 2012 at 11:11 Comment(0)
W
3

The problem is how to specify the test.conf file when running an intergration test using Play's FakeAppication. In my integration test I cannot call play -Dconfig.file=conf/test.conf.

What I managed to do is this:

object FakeSalatApp extends Around {

 def EmbeddedMongoTestPort: Int = 27028

 def inMemoryMongoDatabase(name: String = "default"): Map[String, String] = {
   val dbname: String = "play-test-" + scala.util.Random.nextInt
   Map(
     ("mongodb." + name + ".db" -> dbname),
     ("mongodb." + name + ".port" -> EmbeddedMongoTestPort.toString),
     ("mongodb." + name + ".replicaset.host1.host" -> "localhost"),
     ("mongodb." + name + ".replicaset.host1.port" -> EmbeddedMongoTestPort.toString),
     ("mongodb." + name + ".replicaset.host2.host" -> "localhost"),
     ("mongodb." + name + ".replicaset.host2.port" -> (EmbeddedMongoTestPort + 1).toString),
     ("mongodb." + name + ".replicaset.host3.host" -> "localhost"),
     ("mongodb." + name + ".replicaset.host3.port" -> (EmbeddedMongoTestPort + 2).toString))
  }

 override def around[T <% Result](t: => T) = {
   running(FakeApplication(additionalConfiguration = inMemoryMongoDatabase(), additionalPlugins = Seq("se.radley.plugin.salat.SalatPlugin"))) {
     t // execute t inside a http session
   }
 }
}
Wiper answered 15/11, 2012 at 9:29 Comment(0)
D
10

We had a similar problem loading extra configurations for our integration tests. We found populating maps manually to be tedious so we used the following approach:

private Configuration additionalConfigurations;
@Before
public void initialize(){
    Config additionalConfig = ConfigFactory.parseFile(new File("conf/integration.conf"));
    additionalConfigurations = new Configuration(additionalConfig);
}
@Test
public void testPropertiesGetLoaded() throws Exception{
    running(testServer(3333, fakeApplication(additionalConfigurations.asMap())), HTMLUNIT, new Callback<TestBrowser>(){
        public void invoke(TestBrowser browser){
            String specificProperty = Play.application().configuration().getString("specific.property");
            System.out.println(specificProperty);
        }
    });
}

I don't know if there is a nice method on the Scala side of things, we are doing all our code in java.

Demoralize answered 4/10, 2013 at 14:57 Comment(0)
W
3

The problem is how to specify the test.conf file when running an intergration test using Play's FakeAppication. In my integration test I cannot call play -Dconfig.file=conf/test.conf.

What I managed to do is this:

object FakeSalatApp extends Around {

 def EmbeddedMongoTestPort: Int = 27028

 def inMemoryMongoDatabase(name: String = "default"): Map[String, String] = {
   val dbname: String = "play-test-" + scala.util.Random.nextInt
   Map(
     ("mongodb." + name + ".db" -> dbname),
     ("mongodb." + name + ".port" -> EmbeddedMongoTestPort.toString),
     ("mongodb." + name + ".replicaset.host1.host" -> "localhost"),
     ("mongodb." + name + ".replicaset.host1.port" -> EmbeddedMongoTestPort.toString),
     ("mongodb." + name + ".replicaset.host2.host" -> "localhost"),
     ("mongodb." + name + ".replicaset.host2.port" -> (EmbeddedMongoTestPort + 1).toString),
     ("mongodb." + name + ".replicaset.host3.host" -> "localhost"),
     ("mongodb." + name + ".replicaset.host3.port" -> (EmbeddedMongoTestPort + 2).toString))
  }

 override def around[T <% Result](t: => T) = {
   running(FakeApplication(additionalConfiguration = inMemoryMongoDatabase(), additionalPlugins = Seq("se.radley.plugin.salat.SalatPlugin"))) {
     t // execute t inside a http session
   }
 }
}
Wiper answered 15/11, 2012 at 9:29 Comment(0)
C
2

This is how I did it in Play 2.3.x

  1. Define my application GlobalSettings in a class AppGlobal in a package (not the root package)

    package configs
    
    class AppGlobal extends GlobalSettings {
      // Your application global settings
      ???
    }
    
  2. Define application global settings as object Global extends AppGlobal which is used your application.

  3. In the test class, define a test global. The test configuration is added at the end to override or add to the overall application configuration:

    object TestGlobal extends AppGlobal {
      override def onLoadConfig(config: Configuration, 
                                path: File, 
                                classloader: ClassLoader, 
                                mode: Mode): Configuration = {
        config ++ configuration ++ 
              Configuration.load(path, mode = Mode.Dev, 
                                 Map("config.file" -> "conf/test.conf"))
        }
    }
    
  4. Create the fake application with the above TestGlobal

    FakeApplication(withGlobal = Some(TestGlobal))
    
Castello answered 8/12, 2014 at 2:12 Comment(0)
S
1

In my case I have simply created a base class that all my tests extend. Right before creating the FakeApplication I define the system property config.resource that sets the application's configuration. Then I have structured my configurations as follow :

application.conf : contains no-env specific configurations

test.conf : includes application.conf and defines configurations to run the unit tests

env_local.conf : includes application.conf and defines configurations to run the application locally

env_prod.conf : like env_local.conf but for production etc ...

In my project, for convenience I have crated a script local.sh that simply runs activator -Dconfig.resource=env_local.conf

@RunWith(classOf[JUnitRunner])
class ApplicationTest extends FunSuite with MockitoSugar {
   System.setProperty("config.resource", "test.conf")
   val app = Helpers.fakeApplication()
}
Stambaugh answered 3/7, 2015 at 19:11 Comment(0)
D
-1

Using path won't work here, since this is the path of the FakeApplication you're running (you might have a different path in some cases).

What I'd suggest in your case is to specify a test.conf when running Play for test mode, like e.g.

play -Dconfig.file=conf/test.conf

Then test.conf will be picked up. You could then also have it include your normal application.conf and override just the mongo settings.

Perhaps it would also make sense to have the "single target mode" the default way of connecting to mongodb in your application.conf, and overwrite the mongob configuration to use a replicaset only in a production configuration.

Deliladelilah answered 25/10, 2012 at 6:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.