By Michal Bigos / @teliatko
The phase in software testing in which individual software modules are combined and tested as a group.
Testing business components, in particular, can be very challenging. Often, a vanilla unit test isn't sufficient for validating such a component's behavior. Why is that? The reason is that components in an enterprise application rarely perform operations which are strictly self-contained. Instead, they interact with or provide services for the greater system.
Isolated - Checking one single concern in the system. Usually behavior of one class.
Repeateable - It can be rerun as meny times as you want.
Consistent - Every run gets the same results.
Fast - Because there are loooot of them.
Not isolated - Do not check the component or class itself, but rather integrated components together (sometimes whole application).
Slow - Depend on the tested component/sub-system.
Lack of DI
object CheckIns extends Controller {
  ...
  def generate(pubId: String) = Secured.withBasic { caller: User =>
    Action { implicit request =>
      val pubOpt = PubDao.findOneById(pubId)
      ...
    }
  }
}
						Controller depends directly on DAO
object PubDao extends SalatDAO[Pub, ObjectId](MongoDBSetup.mongoDB("pubs")) {
  ...
}
						 
					 
						Responsibility - encapsulate domain logic
Unit test - testing the correctness of domain logic
 
						Responsibility - read/save model
Integration test - testing the correctness of queries and modifications, with real data and DB
 
						Responsibility - serialize/deserialize model to JSON
Integration test - testing the correctness of JSON output, using the real DAOs
FreeSpecBeforeAndAfter and BeforeAndAfterAll traitsFake application
it should "Test something dependent on Play! application" in {
  running(FakeApplication()) {
    // Do something which depends on Play! application
  }
}
						Real HTTP server
"run in a server" in {
  running(TestServer(3333)) {
    await(WS.url("http://localhost:3333").get).status must equalTo(OK)
  }
}
						Configuration of MongoDB in application
trait MongoDBSetup {
  val MONGODB_URL = "mongoDB.url"
  val MONGODB_PORT = "mongoDB.port"
  val MONGODB_DB = "mongoDB.db"
}
object MongoDBSetup extends MongoDBSetup {
  private[this] val conf = current.configuration
  val url = conf.getString(MONGODB_URL).getOrElse(...)
  val port = conf.getInt(MONGODB_PORT).getOrElse(...)
  val db = conf.getString(MONGODB_DB).getOrElse(...)
  val mongoDB = MongoConnection(url, port)(db)
}
						... another object
Use of MongoDBSetup in DAOs
object PubDao extends SalatDAO[Pub, ObjectId](MongoDBSetup.mongoDB("pubs")) {
  ...
}
						We have to mock or provide real DB to test the DAO
Controllers
object CheckIns extends Controller {
  ...
  def generate(pubId: String) = Secured.withBasic { caller: User =>
    Action { implicit request =>
      val pubOpt = PubDao.findOneById(pubId)
      ...
    }
  }
}
						... you've seen this already
Embedding embedmongo* to ScalaTest
trait EmbedMongoDB extends BeforeAndAfterAll { this: BeforeAndAfterAll with Suite =>
  def embedConnectionURL: String = { "localhost" }
  def embedConnectionPort: Int = { 12345 }
  def embedMongoDBVersion: Version = { Version.V2_2_1 }
  def embedDB: String = { "test" }
  lazy val runtime: MongodStarter = MongodStarter.getDefaultInstance
  lazy val mongodExe: MongodExecutable = runtime.prepare(new MongodConfig(embedMongoDBVersion, embedConnectionPort, true))
  lazy val mongod: MongodProcess = mongodExe.start()
  override def beforeAll() {
    mongod
    super.beforeAll()
  }
  override def afterAll() {
    super.afterAll()
    mongod.stop(); mongodExe.stop()
  }
  lazy val mongoDB = MongoConnection(embedConnectionURL, embedConnectionPort)(embedDB)
}
						*we love recursion in Scala isn't it?
Custom fake application
trait FakeApplicationForMongoDB extends MongoDBSetup { this: EmbedMongoDB =>
  lazy val fakeApplicationWithMongo = FakeApplication(additionalConfiguration = Map(
    MONGODB_PORT -> embedConnectionPort.toString,
    MONGODB_URL -> embedConnectionURL,
    MONGODB_DB -> embedDB
  ))
}
						Trait configures fake application instance for embedded MongoDB instance. MongoDBSetup consumes this values.
Typical test suite class
class DataDrivenMongoDBTest extends FlatSpec 
  with ShouldMatchers 
  with MustMatchers 
  with EmbedMongoDB
  with FakeApplicationForMongoDB {
  ...
}
						Test method which uses mongoDB instance directly
it should "Save and read an Object to/from MongoDB" in {
  // Given
  val users = mongoDB("users") // this is from EmbedMongoDB trait
  // When
  val user = User(username = username, password = password)
  users += grater[User].asDBObject(user)
  // Then
  users.count should equal (1L)
  val query = MongoDBObject("username" -> username)
  users.findOne(query).map(grater[User].asObject(_)) must equal (Some(user))
  // Clean-up
  users.dropCollection()
}
						Test method which uses DAO via fakeApplicationWithMongo
it should "Save and read an Object to/from MongoDB which is used in application" in {
  running(fakeApplicationWithMongo) {
    // Given
    val user = User(username = username, password = password)
    // When
    UserDao.save(user)
    // Then
    UserDao.findAll().find(_ == user) must equal (Some(user))
  }
}
						Example of the full test from controller down to model
class FullWSTest extends FlatSpec with ShouldMatchers with MustMatchers with EmbedMongoDB with FakeApplicationForMongoDB {
  val username = "test"
  val password = "secret"
  val userJson = """{"id":"%s","firstName":"","lastName":"","age":-1,"gender":-1,"state":"notFriends","photoUrl":""}"""
  "Detail method" should "return correct Json for User" in {
    running(TestServer(3333, fakeApplicationWithMongo)) {
      val users = mongoDB("users")
      val user = User(username = username, password = md5(username + password))
      users += grater[User].asDBObject(user)
      val userId = user.id.toString
      val response = await(WS.url("http://localhost:3333/api/user/" + userId)
        .withAuth(username, password, AuthScheme.BASIC)
        .get())
      response.status must equal (OK)
      response.header("Content-Type") must be (Some("application/json; charset=utf-8"))
      response.body must include (userJson.format(userId))
    }
  }
}
						Creating a simple data is easy, but what about collections...
We need easy way to seed them from prepared source and check them afterwards.
Principle
Inspiration - NoSQL Unit, DBUnit
public class WhenANewBookIsCreated {
    @ClassRule
    public static ManagedMongoDb managedMongoDb = newManagedMongoDbRule().mongodPath("/opt/mongo").build();
   	@Rule
    public MongoDbRule remoteMongoDbRule = new MongoDbRule(mongoDb().databaseName("test").build());
    @Test
    @UsingDataSet(locations="initialData.json", loadStrategy=LoadStrategyEnum.CLEAN_INSERT)
    @ShouldMatchDataSet(location="expectedData.json")
    public void book_should_be_inserted_into_repository() {
    	...
    }
}
						Based on JUnit rules or verbose code
This is Java. Example is taken from NoSQL Unit documentation.Goals
Result
it should "Load all Objcts from MongoDB" in {
  mongoDB seed ("users") fromFile ("./database/data/users.json") and
          seed ("pubs") fromFile ("./database/data/pubs.json")
          cleanUpAfter {
    running(fakeApplicationWithMongo) {
      val users = UserDao.findAll()
      users.size must equal (10)
    }
  }
  // Probably will be deprecated in next versions
  mongoDB seed ("users") fromFile ("./database/data/users.json") now()
  running(fakeApplicationWithMongo) {
    val users = UserDao.findAll()
    users.size must equal (10)
  }
  mongoDB cleanUp ("users")
}
						Already implemented
Still in pipeline
mongoexport. Our biggest problem here are Dates (proprietary format).mongo command. To be able to run commands like: db.pubs.ensureIndex({loc : "2d"})We don't like this*
def findCheckInsBetweenDatesInPub(
  pubId: String, 
  dateFrom: LocalDateTime, 
  dateTo: LocalDateTime) : List[CheckIn] = {
  val query = MongoDBObject("pubId" -> new ObjectId(pubId), "created" -> MongoDBObject("$gte" -> dateFrom, "$lt" -> dateTo))
  collection.find(query).map(grater[CheckIn].asObject(_)).toList.headOption
}
						... I cannot read it, can't you?
* and when possible we don't write thisWe like pretty code a lot ... like this:
def findBetweenDatesForPub(pubId: ObjectId, from: DateTime, to: DateTime) : List[CheckIn] = {
  find {
    ("pubId"   ->  pubId) ++
    ("created" $gte from $lt to)
  } sort {
    ("created"  -> -1)
  }
}.toList.headOption
						Casbah query DSL is our favorite ... even when it is not perfect
So we enhanced it:
def findBetweenDatesForPub(pubId: ObjectId, from: DateTime, to: DateTime) : List[CheckIn] = {
    find {
      ("pubId"   $eq  pubId) ++
      ("created" $gte from $lt to)
    } sort {
      "created"  $eq -1
    }
  }.headOption
						Pimp my library again and again...
// Adds $eq operator instead of ->
implicit def queryOperatorAdditions(field: String) = new {
  protected val _field = field
} with EqualsOp
trait EqualsOp {
  protected def _field: String
  def $eq[T](target: T) = MongoDBObject(_field -> target)
}
// Adds Scala collection headOption operation to SalatCursor
implicit def cursorAdditions[T <: AnyRef](cursor: SalatMongoCursor[T]) = new {
  protected val _cursor = cursor
} with CursorOperations[T]
trait CursorOperations[T <: AnyRef] {
  protected def _cursor: SalatMongoCursor[T]
  def headOption : Option[T] = if (_cursor.hasNext) Some(_cursor.next()) else None
}
						