I wish to write unit test for the shipmentNumberValidator method, where Reads is play.api.libs.json.Reads. How do I do it?
object Validator {
def shipmentNumberValidator(): Reads[String] =
Reads.filter(ValidationError(ErrorConstants.BAD_SHIPMENT_NUMBER))(_.matches(BarcodePatterns.ShipmentNumber))
}
The unit test should pass it a shipment number, and the method returns either successfully the shipment number, or the error message if the shipment number format doesn't match. I don't know how to pass values to this method.
The method is usually used in companion objects for implicit reads, e.g.:
object ShipmentOrder {
implicit val shipmentOrderReads: Reads[ShipmentOrder] = (
(JsPath \ "id").read[String](Validator.missingFieldValidator("id") keepAnd Validator.shipmentNumberValidator())(ShipmentOrder.apply _)
}
Depending on the test framework you use (e.g. specs2), you can test expectations as following:
Doing a Json.parse("{expectedJson}") must be JsResult(expectedInstance)
Doing a Json.parse("{unexpectedJson}") must be JsError(_)
Using specs2:
import play.api.libs.json._
"JSON" should {
"be successfully parsed when expected" in {
Json.parse("""{"expected":"json"}""") must beLike[JsResult[ShipmentOrder]] {
case JsSuccess(parsed, _) =>
parsed must_== expectedShipmentOrder
}
}
"fail to be parsed if expected" in {
Json.parse("""{"unexpected":"json"}""") must beLike[JsResult[ShipmentOrder]] {
case JsError(details) =>
ok // possible check `details`
}
}
}
Related
So I have a routes file that looks something like this:
GET /myRes controllers.MyController.get(ids: Option[String], elems: Option[String])
All well and good. Users can get stuff by doing:
/myRes
/myRes?ids=X
/myRes?elems=Y
/myRes?ids=X&elems=Y
However, they can also query the interface by doing:
/myRes?id=X
Which is problematic, because in this case the user is gets the same result as if they had queried /myRes, which is almost certainly not the result they expected. This has been causing a lot of confusion/bugs for developers of the API. Is there an elegant way to catch incorrect/unspecified query parameters being passed to the controller and return a hard error for such queries?
Edit: Changed title to something more descriptive. My problem is basically validating the query parameters to catch any query parameters passed to the API which are not valid/correct.
It can be done with the help of a macro annotation like the following one:
import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
import scala.annotation.compileTimeOnly
import play.api.mvc._
#compileTimeOnly("respond 400 bad request in case of unexpected params")
class StrictParams extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro StrictParamsMacro.impl
}
object StrictParamsMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
annottees.map(_.tree).toList match {
case q"def $name(..$params) = $action { ..$body }" :: Nil =>
val supportedParamNames = params.map(ab => ab.name.toString).toSet
c.Expr[Any](
q"""def $name(..$params) = { req: Request[_] =>
val unsupportedParams = req.queryString.keySet -- $supportedParamNames
if (unsupportedParams.nonEmpty) {
BadRequest(unsupportedParams.mkString("Unsupported Params: ", ", ", ""))
} else {
$body
}
}"""
)
}
}
}
Then you can annotate your action method like this:
#StrictParams
def get(ids: Option[String], elems: Option[String]) = Action {
...
}
i usually pass it like this on get method
GET /getSomething Controllers.Application.getData()
GET /getSomething/:id Controllers.Application.getData(id:Integer)
GET /getSomething/:id/:name Controllers.Application.getData(id:Integer, name :String)
You can define a QueryStringBindable[A] to bind a Map of query string parameters to an instance of type A.
See the corresponding documentation.
Didn't fully explore it yet, but it doesn't seem too difficult to implement a QueryStringBindable[Option[A]].
If you want to forbid confusing parameters (e.g. allowing ids but not id), you can check for unexpected keys in the parameters Map and return an error message if needed (although I would recommand to accept the id key to match the behaviour expected by users).
Hi I want to stub a method with specific parameters and to get the result with a helper method
val myDAOMock = stub[MyDao]
(myDAOMock.getFoos(_:String)).when("a").returns(resHelper("a"))
//btw-is there a way to treat "a" as a parameter to the stubbed method and to the return ?
(myDAOMock.getFoos(_:String)).when("b").returns(resHelper("b"))
def resHelpr(x:String) = x match{
case "a" => Foo("a")
case "b" => Foo("b")
}
but it seems that on my test I can capture only one since the 2nd test fails (regardless to the order that I run the tests )
"A stub test" must{
"return Foo(a)" in{
myDAOMock.getFoos("a")
}
"return Foo(b)" in{
myDAOMock.getFoos("b") //this one will fail on null pointer exception
}
how can I improve my stubbing ?
I refactored your example a bit. I believe your issue is that the stubs for getFoos need to be defined within your tests.
import org.scalamock.scalatest.MockFactory
import org.scalatest._
class TestSpec extends FlatSpec with Matchers with MockFactory {
val myDAOMock = stub[MyDao]
val aFoo = Foo("a")
val bFoo = Foo("b")
def resHelper(x: String): Foo = {
x match {
case "a" => aFoo
case "b" => bFoo
}
}
"A stub test" must "return the correct Foo" in {
(myDAOMock.getFoos(_: String)) when "a" returns resHelper("a")
(myDAOMock.getFoos(_: String)) when "b" returns resHelper("b")
assert(myDAOMock.getFoos("a") === aFoo)
assert(myDAOMock.getFoos("b") === bFoo)
}
}
I think this was an issue in older versions of ScalaMock and should now be fixed in later versions, returning a better message instead of the NPE. The NPE happens as you re-used a mock in two cases.
See http://scalamock.org/user-guide/sharing-scalatest/ how to do that safely.
I have a class with a single method that I want to unit-test:
#Singleton
class RegistrationWorkflow #Inject()(userService: UserService,
addUserValidator: RegisterUserValidator,
db: Database) {
def registerUser(registerForm: RegisterUserForm): Future[Vector[FormError]] = {
val dbActions = addUserValidator.validate(registerForm).flatMap({ validation =>
if (validation.isEmpty) {
userService.add(User(GUID.shortGuid(),
registerForm.username,
registerForm.email,
BCrypt.hashpw(registerForm.password, BCrypt.gensalt())))
.andThen(DBIO.successful(validation))
} else {
DBIO.successful(validation)
}
}).transactionally
db.run(dbActions)
}
}
addUserValidator validates the form and returns a Vector of form errors. If there were no errors, the user is inserted into database. I am returning the form errors because in the controller I'm either returning a 201 or a 400 with a list of errors.
I have written a specs2 test for this:
class RegistrationWorkflowTest extends Specification with Mockito with TestUtils {
"RegistrationControllerWorkflow.registerUser" should {
"insert a user to database if validation succeeds" in new Fixture {
registerUserValidatorMock.validate(testUserFormData) returns DBIO.successful(Vector())
userServiceMock.add(any) returns DBIO.successful(1)
val result = await(target.registerUser(testUserFormData))
result.isEmpty must beTrue
there was one(registerUserValidatorMock).validate(testUserFormData)
there was one(userServiceMock).add(beLike[User] { case User(_, testUser.username, testUser.email, _) => ok })
}
"return error collection if validation failed" in new Fixture {
registerUserValidatorMock.validate(testUserFormData) returns DBIO.successful(Vector(FormError("field", Vector("error"))))
val result = await(target.registerUser(testUserFormData))
result.size must beEqualTo(1)
result.contains(FormError("field", Vector("error"))) must beTrue
there was one(registerUserValidatorMock).validate(testUserFormData)
there was no(userServiceMock).add(any)
}
}
trait Fixture extends Scope with MockDatabase {
val userServiceMock = mock[UserService]
val registerUserValidatorMock = mock[RegisterUserValidator]
val target = new RegistrationWorkflow(userServiceMock, registerUserValidatorMock, db)
val testUser = UserFactory.baseUser()
val testUserFormData = RegisterUserFactory.baseRegisterUserForm()
}
}
The issue with this test is that it just asserts that userService.add was called. This means that I can change my implementation to the following:
val dbActions = addUserValidator.validate(registerForm).flatMap({ validation =>
if (validation.isEmpty) {
userService.add(User(GUID.shortGuid(),
registerForm.username,
registerForm.email,
BCrypt.hashpw(registerForm.password, BCrypt.gensalt())))
DBIO.successful(validation)
} else {
DBIO.successful(validation)
}
}).transactionally
db.run(dbActions)
The test still passes, but the user will not be inserted, because I am not ussing andThen combinator on the DBIO that was returned by userService.add method.
I know that I could use an in memory database and then assert that the user was actually inserted, but I don't want to do that because I already tested userService.add method separately with an in memory database and now I want to test registerUser method without calling any dependencies.
I'd like to test a method that returns a Future. My attempts were as follows:
import org.specs2.mutable.Specification
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
class AsyncWebClientSpec extends Specification{
"WebClient when downloading images" should {
"for a valid link return non-zero content " in {
val testImage = AsyncWebClient.get("https://www.google.cz/images/srpr/logo11ww.png")
testImage.onComplete { res =>
res match {
case Success(image) => image must not have length(0)
case _ =>
}
AsyncWebClient.shutDown
}
}
}
}
Apart from the fact that I am unable to make this code work I guess that there could be a better way of testing a futures with a Future-oriented matcher.
How to do it properly in specs2?
You can use the Matcher.await method to transform a Matcher[T] into a Matcher[Future[T]]:
val testImage: Future[String] =
AsyncWebClient.get("https://www.google.cz/images/srpr/logo11ww.png")
// you must specify size[String] here to help type inference
testImage must not have size[String](0).await
// you can also specify a number of retries and duration between retries
testImage must not have size[String](0).await(retries = 2, timeout = 2.seconds)
// you might also want to check exceptions in case of a failure
testImage must throwAn[Exception].await
Took me awhile to find this so thought I'd share. I should've read the release notes. In specs2 v3.5, it is required to use implicit ExecutionEnv to use await for future. This can also be used for future transformation (i.e. map) see http://notes.implicit.ly/post/116619383574/specs2-3-5.
excerpt from there for quick reference:
import org.specs2.concurrent.ExecutionEnv
class MySpec extends mutable.Specification {
"test of a Scala Future" >> { implicit ee: ExecutionEnv =>
Future(1) must be_>(0).await
}
}
Await is an anti pattern. Shouldn't ever use it.
You can use traits like ScalaFutures, IntegrationPatience, and Eventually.
whenReady does the magic you are looking for.
Example:
import org.specs2.mutable.Specification
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
import scala.concurrent.Future
class AsyncWebClientSpec extends Specification
with ScalaFutures
with IntegrationPatience {
"WebClient when downloading images" should {
"for a valid link return non-zero content " in {
whenReady(Future.successful("Done")){ testImage =>
testImage must be equalTo "Done"
// Do whatever you need
}
}
}
}
There is a nice thing for that in specs2 - implicit await method for Future[Result]. If you take advantage of future transformations you can write like this:
"save notification" in {
notificationDao.saveNotification(notification) map { writeResult =>
writeResult.ok must be equalTo (true)
} await
}
Future composition comes to the rescue when some data arrangement with async functions is needed:
"get user notifications" in {
{
for {
_ <- notificationDao.saveNotifications(user1Notifications)
_ <- notificationDao.saveNotifications(user2Notifications)
foundUser1Notifications <- notificationDao.getNotifications(user1)
} yield {
foundUser1Notifications must be equalTo (user1Notifications)
}
} await
}
Note how we have to use an additional block around for-comprehension to convince compiler. I think it's noisy, so if we turn await method in a function we come up with a nicer syntax:
def awaiting[T]: Future[MatchResult[T]] => Result = { _.await }
"get user notifications" in awaiting {
for {
_ <- notificationDao.saveNotifications(user1Notifications)
_ <- notificationDao.saveNotifications(user2Notifications)
foundUser1Notifications <- notificationDao.getNotifications(user1)
} yield {
foundUser1Notifications must be equalTo (user1Notifications)
}
}
Wondering why #etorreborre did not mention "eventually"
See https://github.com/etorreborre/specs2/blob/master/tests/src/test/scala/org/specs2/matcher/EventuallyMatchersSpec.scala#L10-L43
class EventuallyMatchersSpec extends Specification with FutureMatchers with ExpectationsDescription { section("travis")
addParagraph { """
`eventually` can be used to retry any matcher until a maximum number of times is reached
or until it succeeds.
""" }
"A matcher can match right away with eventually" in {
1 must eventually(be_==(1))
}
"A matcher can match right away with eventually, even if negated" in {
"1" must not (beNull.eventually)
}
"A matcher will be retried automatically until it matches" in {
val iterator = List(1, 2, 3).iterator
iterator.next must be_==(3).eventually
}
"A matcher can work with eventually and be_== but a type annotation is necessary or a be_=== matcher" in {
val option: Option[Int] = Some(3)
option must be_==(Some(3)).eventually
}
onComplete returns Unit, so that block of code returns immediately and the test ends before being able to do anything. In order to properly test the result of a Future, you need to block until it completes. You can do so using Await, and setting a maximum Duration to wait.
import scala.concurrent._
import scala.concurrent.duration._
Await.result(testImage, Duration("10 seconds")) must not have length(0)
I want to verify if the business logic passes the expected user object to dao, but I can't figure how to write an custom argument matcher for it.
"user" should {
"be saved" in {
val dao = new UserDao()
dao.save(any[User]) returns mock[User]
runMyBusinessLogic();
val expectedUser = new User("Freewind", 123.23234)
there was one(dao).save(mymatcher(expectedUser));
}
}
The User class:
case class User(name:String, random: Double)
Which contains a double field, that I need to do some special comparison for it.
The mymatcher is the matcher I want to define:
def mymatcher(expected: User) = ??? {
// compare `name` and `random`
}
But I don't know how to do it in spec2, and can't find any useful documents. Any helps?
I use beLike matcher. Like this:
one(daoMock).insert { beLike[MyEntity] { case t:Entity => {
t.summary mustEqual "Summary"
t.description mustEqual "Description"
}}}
Inside beLike matcher you could use ordinary value matchers.
For mockito matching I used Matchers.argThat
import org.mockito.Matchers
import org.mockito.Mockito.verify
verify(service).methodCall(Matchers.argThat({
case CaseClass("const", arg2) =>
arg2 == expected
case _ => false
}))