Parallel MongoRecord test execution with ScalaTest
Jun 8, 2013A few weeks ago, I finally figured out a way to enable parallel test execution of Lift MongoRecords using ScalaTest. The problem is when you call MongoDB.defineDb
the entry is added to a Map with the MongoIdentifier as the key. So, you can’t add more than one entry for the same MongoIdentifer, which means you can’t use a different database for testing without doing them one at a time.
The solution is rather simple and involves Lift’s Dependency Injection mechanism. It allows us to use a different MongoIdentifier for each test suite.
First we need to define an injectable MongoIdentifier to add to our model’s MongoMetaRecord. I put this in a MongoConfig object:
package code
package config
import net.liftweb._
import http.Factory
import mongodb._
object MongoConfig extends Factory {
val identifier = new FactoryMaker[MongoIdentifier](DefaultMongoIdentifier) {}
...
}
Next we need to override def mongoIdentifier
in our MongoMetaRecord objects. I create a custom MongoMetaRecord trait where I do this and mix that into my models instead of MongoMetaRecord:
package code
package lib
import code.config.MongoConfig
import net.liftweb._
import mongodb.record._
/**
* A custom MongoMetaRecord that adds an injectable MongoIdentifier.
*/
trait MyMetaRecord[A <: MongoRecord[A]] extends MongoMetaRecord[A] {
this: A =>
override def mongoIdentifier = MongoConfig.identifier.vend
}
Here’s an example model that uses the above trait:
package code
package model
import code.lib.MyMetaRecord
import scala.xml._
import net.liftweb._
import common._
import mongodb.record._
import record.field._
class Company private () extends MongoRecord[Company] with ObjectIdPk[Company] {
def meta = Company
object name extends StringField(this, 256) {
override def displayName = "Name"
override def validations =
valMinLen(3, "Must be at least 3 characters") _ ::
valMaxLen(256, "Must be 256 characters or less") _ ::
super.validations
}
}
object Company extends Company with MyMetaRecord[Company] {
import mongodb.BsonDSL._
override def collectionName = "main.companies"
ensureIndex((name.name -> 1), true)
}
Now, you can use the Mongo Suite’s defined in MongoTestKit.scala to create some traits to use with your tests:
package code
import config.MongoConfig
import org.scalatest._
import org.scalatest.matchers.ShouldMatchers
import net.liftweb._
import common._
import http._
import util._
import Helpers._
trait BaseWordSpec extends WordSpec with ShouldMatchers
trait BaseMongoWordSpec extends BaseWordSpec with MongoSuite {
def mongoIdentifier = MongoConfig.identifier
}
trait BaseMongoSessionWordSpec extends BaseWordSpec with MongoSessionSuite {
def mongoIdentifier = MongoConfig.identifier
}
trait WithSessionSpec extends AbstractSuite { this: Suite =>
protected def session = new LiftSession("", randomString(20), Empty)
abstract override def withFixture(test: NoArgTest) {
S.initIfUninitted(session) { super.withFixture(test) }
}
}
And, finally, write some tests:
package code
package model
class CompanySpec extends BaseMongoSessionWordSpec {
"Company" should {
...
}
}
The above code is available in the lift-mongo.g8 giter8 template.