Search code examples
scalascalatestscalacheck

There is no started application when trying to use beforeAll in scalatest


in my test class I want to do something before all the tests begin, so I did something like this:

class ApplicationSpec extends FreeSpec with OneServerPerSuite with BeforeAndAfterAll {

  override protected def beforeAll(): Unit = {
    doSomething()
  }

  "Application Test" - {
    "first test" in {
      ...
    }
  }

}

but I get an error:

Exception encountered when invoking run on a nested suite - There is no started application java.lang.RuntimeException: There is no started application

it only works if i try to use doSomething() inside the tests...

how can i fix this?

thanks!


Solution

  • I am assuming doSomething() performs some operation that is dependent on application state.

    Try this:

    class ApplicationSpec extends FreeSpec with BeforeAndAfterAll with OneServerPerSuite{
    
      override protected def beforeAll(): Unit = {
        doSomething()
      }
    
      "Application Test" - {
        "first test" in {
          ...
        }
      }
    
    }
    

    The problem is you are possibly mixin linearization in wrong order. By mixin OneSerPerSuite before BeforeAndAfterAll, the order in which the super.run() is called is reversed, leading to beforeAll() being called before Application start.

    From the git repo of the two projects:

     //BeforeAndAfterAll
     abstract override def run(testName: Option[String], args: Args): Status = {
        if (!args.runTestInNewInstance && (expectedTestCount(args.filter) > 0 || invokeBeforeAllAndAfterAllEvenIfNoTestsAreExpected))
          beforeAll()
    
        val (runStatus, thrownException) =
          try {
            (super.run(testName, args), None)
          }
          catch {
            case e: Exception => (FailedStatus, Some(e))
          }
        ...
       }
    
    
        //OneServerPerSuite
        abstract override def run(testName: Option[String], args: Args): Status = {
        val testServer = TestServer(port, app)
        testServer.start()
        try {
          val newConfigMap = args.configMap + ("org.scalatestplus.play.app" -> app) + ("org.scalatestplus.play.port" -> port)
          val newArgs = args.copy(configMap = newConfigMap)
          val status = super.run(testName, newArgs)
          status.whenCompleted { _ => testServer.stop() }
          status
        } catch { // In case the suite aborts, ensure the server is stopped
          case ex: Throwable =>
            testServer.stop()
            throw ex
        }
      }
    

    So by putting the OneServerPerSuite trait in the end, it will first initialize the application, then call super.run() which will call the run method inside BeforeAndAfterAll which will execute beforeAll() and then invoke super.run() of FreeSpec which will execute the tests.