Giter Site home page Giter Site logo

play2-reactivemongo-mocks's Introduction

play2-reactivemongo-mocks

Library to allow mocking of a Play-ReactiveMongo app's MongoDB back end.

The intention of this library is to facilitate the writing of tests which:

  • Cover DAO- or Service-level logic around backend database calls
  • Allow a level of component testing beyond simple unit tests
  • Execute far faster than conventional "seeded database" tests
  • Eliminate the potential issues around inconsistent and/or shared database state

A Note About Versioning

Due to the nature of mocking method calls, it is essential that the version of this library matches exactly the version of [https://github.com/ReactiveMongo/Play-ReactiveMongo].

Installation

Bring in the library by adding the following to your Play project's build.sbt.

  • The release repository:
   resolvers ++= Seq(
     "Millhouse Bintray"  at "http://dl.bintray.com/themillhousegroup/maven"
   )
  • The dependency itself:
Play 2.4.x:
   libraryDependencies ++= Seq(
     "com.themillhousegroup" %% "play2-reactivemongo-mocks" % "0.11.9_0.5.35"
   )

The above version (the 0.5.x family) is for Play-ReactiveMongo 0.11.9 and Play 2.4. As Play 2.4 is for Scala 2.11, this library is correspondingly now only for Scala 2.11.

Play 2.5.x:
   libraryDependencies ++= Seq(
     "com.themillhousegroup" %% "play2-reactivemongo-mocks" % "0.11.11_0.7.42"
   )

The 0.6.x family was required for Play 2.5.x support and moves the expected ReactiveMongo version to 0.11.11. The 0.7.x family fixes deprecation warnings in the underlying ReactiveMongo library.

Older Versions

The 0.4.x versions of this library incorrectly specified a very old version of the Specs2 library; this was reported in Issue 2 and resulted in the 0.5.x line. All 0.4.x versions should be considered deprecated; upgrading to 0.5.x is painless and it is completely backwards-compatible.

If you are using Play 2.3, you can still use the older published 0.3.x versions of this library, which target Play-ReactiveMongo 0.10.5.0.akka23, in both Scala 2.10 and Scala 2.11 flavours. Substitute the following dependency:

	"com.themillhousegroup" %% "play2-reactivemongo-mocks" % "0.10.5.0.akka23_0.3.11"

Usage

Two modes of usage exist - the first will be more familiar to those who have used mocking libraries such as Mockito before. The second is a "higher-level" abstraction over the Mongo datastore, where the entire store (or at least, the part(s) being tested) are represented as a Map of Maps. If you are executing complex multi-Collection join-like queries, it may end up being a more readable solution.

Traditional Mock Definitions

The code to be tested

Let's say you have a simple Controller that uses ReactiveMongo just like in their sample tutorial:

class PersonController @Inject() (val reactiveMongoApi: ReactiveMongoApi) 
	extends Controller with MongoController with ReactiveMongoComponents {

	def collection: JSONCollection = db.collection[JSONCollection]("persons")

	def findByName(name: String) = Action.async {
    val cursor: Cursor[JsObject] = collection.
      find(Json.obj("name" -> name)).
      sort(Json.obj("created" -> -1)).
      cursor[JsObject]

    val futurePersonsList: Future[List[JsObject]] = cursor.collect[List]()

    val futurePersonsJsonArray: Future[JsArray] =
      futurePersonsList.map { persons => Json.arr(persons) }

    futurePersonsJsonArray.map { persons =>
      Ok(persons)
    }
  }
}

Great. You can run this against a real Mongo instance and it works fine. Now let's add some test coverage.

Define a Spec

Firstly, just mix in MongoMocks into our standard Specs2 specification:

import org.specs2.mutable.Specification
import com.themillhousegroup.reactivemongo.mocks.MongoMocks

class PersonControllerSpec extends Specification with MongoMocks {

}

Defining global behaviour

If you just want your mock persistence layer to always reply with meaningful results, use the mockedCollection(name) method together with one or more of the givenMongo... methods, like this:

class PersonControllerSpec extends Specification with MongoMocks {
    val mockedPersons = mockedCollection("persons")(this.mockDB)
    val people = List(
      Json.obj("name" -> "Alice", "created" -> 123456789L),
      Json.obj("name" -> "Bob", "created" -> 333333389L)
    )
    givenMongoCollectionFindAnyReturns(mockedPersons, people)

		val testController = new PersonController
		
		"some test of person finding" in {
			
		} 
}

The Map Abstraction

Mix in MappedMongoMocking and define mockData - a Map[String, Set[JsObject] i.e. mapping CollectionName to its contents. That's it! All of your Mongo find() operations will now work against this data.

Credits

Standing on the shoulders of giants:

play2-reactivemongo-mocks's People

Contributors

themillhousegroup avatar

Stargazers

Marcin Gosk avatar Cameron McKay avatar Maxime Leprince avatar Danny Thuering avatar

Watchers

 avatar  avatar

play2-reactivemongo-mocks's Issues

JsonCollection is final class in version 0.12

JSONCollection cannot be mocked in the latest version of play-reactivemongo ๐Ÿ‘Ž

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class reactivemongo.play.json.collection.JSONCollection
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types
	at org.specs2.mock.MockitoMocker$class.mock(MockitoMocker.scala:21)

Cast problems with BSONCollection

Hello,
First of all i want say thanks for your work :)
I'm trying to use your library to mock reactivemongo but I used the BSONCollection as type for db collection so i come up with this error when i try to run some test.
java.lang.ClassCastException: play.modules.reactivemongo.json.collection.JSONCollection$$EnhancerByMockitoWithCGLIB$$98c2d9b2 cannot be cast to reactivemongo.api.collections.bson.BSONCollection

There's a workaround for this?
If it can help, here there's the stack trace: http://pastebin.com/4JQWDHWB
and here the code of my test: https://github.com/benkio/DomoticRoom/blob/Server/Server/test/persistenceStore/persistenceStoreTest.scala

Using implicit json format

Would it be possible to use code that implicitly do the conversion in json like this:

case class User( firstName: String, lastName: String)

object JsonFormats {
  import play.api.libs.json.Json
  implicit val userFormat = Json.format[User]
}
request.body.validate[User].map { user =>
      collection.insert(user).map { lastError =>
        Logger.debug(s"Successfully inserted with LastError: $lastError")
        Created
      }
    }.getOrElse(Future.successful(BadRequest("invalid json")))

I get this error like it doesn't use the implicit writer at all?

[error]    models.User cannot be cast to play.api.libs.json.JsObject (CollectionInsert.scala:28)
[error] com.themillhousegroup.reactivemongo.mocks.facets.CollectionInsert$$anonfun$setupMongoInserts$4.apply(CollectionInsert.scala:28)
[error] com.themillhousegroup.reactivemongo.mocks.facets.CollectionInsert$$anonfun$setupMongoInserts$4.apply(CollectionInsert.scala:27)
[error] org.mockito.internal.stubbing.StubbedInvocationMatcher.answer(StubbedInvocationMatcher.java:34)
[error] org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:91)
[error] org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
[error] org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:38)
[error] org.mockito.internal.creation.cglib.MethodInterceptorFilter.intercept(MethodInterceptorFilter.java:59)
[error] play.modules.reactivemongo.json.collection.JSONCollection$$EnhancerByMockitoWithCGLIB$$e509bfae.insert(<generated>)
[error] controllers.UserController$$anonfun$createFromJson$1$$anonfun$apply$1.apply(UserController.scala:32)
[error] controllers.UserController$$anonfun$createFromJson$1$$anonfun$apply$1.apply(UserController.scala:31)
[error] play.api.libs.json.JsResult$class.map(JsResult.scala:77)
[error] play.api.libs.json.JsSuccess.map(JsResult.scala:9)
[error] controllers.UserController$$anonfun$createFromJson$1.apply(UserController.scala:31)
[error] controllers.UserController$$anonfun$createFromJson$1.apply(UserController.scala:30)
[error] play.api.mvc.Action$.invokeBlock(Action.scala:533)
[error] play.api.mvc.Action$.invokeBlock(Action.scala:530)
[error] play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:493)
[error] play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105)
[error] play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105)
[error] play.utils.Threads$.withContextClassLoader(Threads.scala:21)
[error] play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:104)
[error] play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:103)
[error] play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:103)
[error] play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:96)
[error] play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:524)
[error] play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:524)
[error] play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:560)
[error] play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:560)
[error] play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:537)
[error] play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:537)
[error] akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40)
[error] akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397)

Would it be possible to use the original implicit writer passed to function instead of this default value?

targetCollection.insert(insertMatcher, anyWriteConcern)(anyPackWrites, anyEC) answers { args =>
      val o: JsObject = firstArg(args)
      logger.debug(s"Insert of object $o will be considered a ${bool2Success(ok)}")
      Future.successful(mockResult(ok))
    }

def anyPackWrites = Matchers.any[JSONSerializationPack.Writer[JsObject]]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.