My function under test returns None or Some(ObjectOfSignupEmail). In my test case, I want to match that the returned value is Some(ArgumentMatchers.any[SignupEmail]) but I get error
Expected :Some(null)
Actual :Some(SignupEmail(Welcome,Test<mailrobot#test.com>,<a href=https://localhost:9000/test/ws/users/signup/11111111-1111-1111-1111-111111111111>Click here to verify email</a>))
If I change the code to signupEmailOption mustBe Some(expectedAnswer) where expectedAnswer is an instance of SignupEmail then the test passes.
Why ArgumentMatchers.any didn't work inside Some?
This doesn't work
"createEmailMessageForUserToken for all correct parameters" should {
"return Some(email)" in {
val mailConfig = Map("signupUrl"-> "/test/ws/users/signup/",
"signupFrom"->"Test<mailrobot#test.com>",
"signupReply"->"Test<noreply#test.com>",
"signupSubject"->"Welcome")
val mailerConfig = Map(
"host" -> "localhost", // (mandatory). The domain of mail server i.e. the server is responsible for sending/receiving emails for this domain
"port" -> "9000",
"tlsRequired" -> "yes"
)
val newConfig = Map("mail"->mailConfig,
"play.mailer"->mailerConfig)
val newConfiguration = Configuration.from(newConfig)
val testEnv = new TestEnv(newConfiguration)
val signupEmailOption:Option[SignupEmail] = testEnv.controller.createEmailMessageForUserToken(testEnv.userToken)
signupEmailOption mustBe Some(ArgumentMatchers.any(SignupEmail.getClass()))
}
}
This works
"createEmailMessageForUserToken for all correct parameters" should {
"return Some(email)" in {
val mailConfig = Map("signupUrl"-> "/test/ws/users/signup/",
"signupFrom"->"Test<mailrobot#test.com>",
"signupReply"->"Test<noreply#test.com>",
"signupSubject"->"Welcome")
val mailerConfig = Map(
"host" -> "localhost", // (mandatory). The domain of mail server i.e. the server is responsible for sending/receiving emails for this domain
"port" -> "9000",
"tlsRequired" -> "yes"
)
val newConfig = Map("mail"->mailConfig,
"play.mailer"->mailerConfig)
val newConfiguration = Configuration.from(newConfig)
val testEnv = new TestEnv(newConfiguration)
val url = "https://" + mailerConfig("host") + ":" + mailerConfig("port") + mailConfig("signupUrl") + testEnv.userToken.tokenId
val html =s"<a href=${url}>Click here to verify email</a>"
//println("html is "+html)
val expectedAnswer = SignupEmail(mailConfig("signupSubject"),mailConfig("signupFrom"),html)
println("expected answer would be "+expectedAnswer)
val signupEmailOption:Option[SignupEmail] = testEnv.controller.createEmailMessageForUserToken(testEnv.userToken)
signupEmailOption mustBe Some(expectedAnswer)
// signupEmailOption mustBe Some(ArgumentMatchers.any(SignupEmail.getClass()))
}
}
You have to use Scalatest matchers instead of Mockito matchers for what you want to do
You are mixing concepts, mockito matchers are meant to be used in arguments of stubbed mock methods, if you want to assert the result of an invocation to your test object you have to use the matchers provided by your test framework (Scalatest in your case for what I can see), so basically check this page for the docs of mustBe and Options.
A hint: if you want to check the type of whatever is inside the option, you can use a partial function matcher and write something like
signupEmailOption should matchPattern { case Some(_: SignupEmail) => }
A few options.
signupEmailOption should not be None
signupEmailOption should not be empty
signupEmailOption shouldBe defined
signupEmailOption should matchPattern { case Some(_) => }
inside(signupEmailOption) { case Some(_) => }
These are all equivalent.
But what you are doing - signupEmailOption shouldBe Some(expectedAnswer) is actually the best option of all. It is the right thing to do here. Just keep it like that.
Note: should* and must* assertions are pretty much the same thing, they just depend on which DSL trait you mix in.
Related
I try to write a unit test for a small function in a controller using Play/ScalaTest+ Play. The function I want to test looks like this:
def functionToTest(id: String) = Action.async {
implicit request =>
lang
deeperFunction{ implicit context =>
...
}
}
The deeperFunction
def deeperFunction(block: Context => Future[Result])(implicit request: RequestHeader): Future[Result] = {
// returns Future.successful(Found("DummyUrltoRedirect"))
}
}
The deeperFunction is inherited from a trait and I don't want use the real one here because it's a unit test and so I want to use a matcher instead
val deeperMock = mock[Rainmaker]
val contextMock = mock[Context]
val controller = new Controller()(.....) // list of implicit arguments
"Controller" must {
"return something" in {
val request = FakeRequest("GET", "/something")
when(deeperMock.deeperFunction(anyObject)(anyObject)) thenReturn Future.successful(Found("DummyUrlToRedirect"))
val id = "id"
val result = controller.functionToTest(id).apply(request)
status(result) mustBe Ok
}
}
But when I run this, the line "val result = controller.functionToTest(id).apply(request)" still seems to call the real deeperFunction, not the fake one and therefore throws a null matcher at some point.
I also tried to use
when(controller.deeperFunction(anyObject)(anyObject)) thenReturn Future.successful(Found("DummyUrlToRedirect"))
instead, because the deeperFunction is inherited, but with the same result.
I tried to stick to theses instructions
ScalaTest+Play
dzone
but it seems I am still missing some basics/understanding. Thanks in advance.
I am migrating from code spray (version: 1.3.4) to akka-http (version: 10.0.13).
We have an existing clients they send Content-Type like application/vnd.awesome.value; mykey="custom/custom"
Following code works fine in spray but in akka-http parameter (mykey) is stripped off
request.header[`Content-Type`].map { header =>
// HERE PARAMS IS EMPTY MAP
val myKeyValue = header.contentType.mediaType.params.get("mykey").map(_.replace("\"", ""))
myKeyValue.flatMap(_.toMediaType).getOrElse(header.contentType.mediaType)
}.getOrElse(defaultMediaType)
StringToMediaTypeConversion.scala
val customMediaTypes: List[MediaType] = ???
implicit class StringToMediaType(private val str: String) extends AnyVal {
private def parseMediaType(input: String): Option[MediaType] = MediaType.parse(input).fold(_ => None, Some(_))
private def isSupportedType(mediaType: MediaType): Boolean = customMediaTypes.contains(mediaType)
private def extractMediaTypeParamIfExists(mediaType: MediaType): Option[MediaType] = mediaType.params.get("mykey") match {
case Some(value) => parseMediaType(value)
case None => Some(mediaType)
}
def toMediaType: Option[MediaType] = parseMediaType(str).flatMap(extractMediaTypeParamIfExists).filter(isSupportedType)
}
Following unit test is working is passing
val customMediaType: Option[MediaType] = """application/vnd.awesome.value; mykey="custom/custom""""".toMediaType
customMediaType should not be None
customMediaType.get.mainType shouldBe "custom"
customMediaType.get.subType shouldBe "custom"
Any help is appreciated.
How to extract raw value? or How to extract param value of media type?
I was registering custom media types as specified in the documentation, if I remove it I am seeing expected behaviour.
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.
Play Framework v 2.1 with scala, I am trying to test my ProductController with invalid call (missing parameters), I am suppose to get BadRequest response..
Controller code is return Ok(Json.toJson(some class..)) or BadRequest(Json.toJson(some class..)) incase something went wrong.
I Defined the test class:
class ProductApplicationTests extends PlaySpecification
with Mockito
with ProductServiceComponent
{
lazy val productService = mock[ProductService]
... other things
def app = FakeApplication(
withoutPlugins = Seq("com.typesafe.plugin.RedisPlugin"),
withGlobal = Some(
new GlobalSettings {
override def getControllerInstance[A](clazz: Class[A]) = clazz match {
case c if c.isAssignableFrom(classOf[ProductController]) => new ProductController(ProductApplicationTests.this).asInstanceOf[A]
case _ => super.getControllerInstance(clazz)
}
}
)
)
def mockStuff = {
productService.addProduct(any[AddProductRequest]) returns
DTOResponse(product.id.get)
productService.updateProduct(any[UpdateProductRequest]) returns
DTOResponse(product.id.get)
productService.deleteProduct(any[DeleteProductRequest]) returns
DTOResponse(product.id.get)
}
step(mockStuff)
"Application" should {
"Get product with no tenant Id" in {running(FakeApplication()) {
val req = FakeRequest(method = "POST", uri = routes.ProductController.getProducts("en_US", "1", "1").url,
headers = FakeHeaders(
Seq("Content-type"->Seq("application/json"))
),
body = None
)
val Some(result) = route(req)
status(result) must equalTo(400)
Problem:
I get error:
Cannot write an instance of None.type to HTTP response. Try to define a Writeable[None.type]
I have followed this post: Play 2 - Scala FakeRequest withJsonBody And i dont understand what im doing wrong..
When i send this code as test it works..
"Get product with valid parameters" in new WithApplication(app) {
val result = route(FakeRequest(GET, "/v1.0/products?lang=en_US&t=1&ids=1,2"))
result must beSome
status(result.get) must equalTo(OK)
contentType(result.get) must beSome.which(_ == "application/json")
contentAsString(result.get) must contain("products")
contentAsString(result.get) must contain("notFoundIds")
}
Thanks for any comment/answers..
By the way my global.scala looks like:
override def onBadRequest(request: RequestHeader, error: String) = {
var errorResponse:ErrorResponse[String] = ErrorResponse(ErrorCode.GeneralError, "Error processing request", 500)
errorResponse.addMessage(error)
Future.successful(BadRequest(Json.toJson(errorResponse)))
}
override def onError(request: RequestHeader, ex: Throwable) =
{
var errorResponse:ErrorResponse[String] = ErrorResponse(ErrorCode.GeneralError, "Error processing request", 500)
errorResponse.addMessage(ex.getMessage)
Future.successful(BadRequest(Json.toJson(errorResponse)))
}
And if i run Get in RestAPI test client i get:
{"code":100,"message":"Error processing request - Missing parameter: t"}
If you take a look at the source for FakeRequest, you'll notice that you need to set the body to AnyContentAsEmpty. On a sidenote, should that first test method be a POST? Your other examples seem to be GETs.
As for your second issue with running Get in RestAPI tet client, the error message seems pretty clear, you need to provide the parameter t as you did in the previous example val result = route(FakeRequest(GET, "/v1.0/products?lang=en_US&t=1&ids=1,2"))
I have a method in my controller that I'd like to call directly. It takes in a POSTed form, validates it, then returns something. I want to test this directly - i.e. not go through the routes helper.
This is my Form code (FormFields is just a case class)
val searchForm = Form(
mapping(
"foo" -> nonEmptyText,
"filter" -> optional(text).verifying("Filter text must be 'published' or 'unpublished'",
x => x.isEmpty || x.get == "published" || x.get == "unpublished")
)(FormFields.apply)(FormFields.unapply)
)
This is my controller call.
def doThings() = IsAuthenticated {
username => implicit request => {
searchForm.bindFromRequest().fold(
formWithErrors => BadRequest(s"Incorrect data: ${formWithErrors.errors.map(x => s"${x.key} ${x.message}").mkString}."),
form => {
OK("Test text here")
}
)
}
}
If I call this via my routes file, as below - This works as expected. Form gets posted, verified, returns OK("Test...") as expected.
ie. The below works (using Specs2)
val request = FakeRequest(POST, "notarealurl")
.withFormUrlEncodedBody(
"filter" -> "published",
"foo" -> "Test"
).withSession("email" -> "testuser")
val Some(result) = route(request)
status(result) must equalTo(OK)
However, whatever I try to call the method directly fails - The failure happens on the form validation step. It tells me that "foo" is missing a value when I run the unit test. This is how I'm attempting to do this.
val request = FakeRequest()
.withFormUrlEncodedBody(
"filter" -> "published",
"foo" -> "Test"
).withSession("email" -> "testuser")
//val Some(result) = route(request)
val result = Search.searchProducts()(request)
println(contentAsString(result))
status(result) must equalTo(OK)
The text printed is "Incorrect search: foo error.required." I think I'm not doing the call properly, but I don't know where I'm going wrong.
Note : The code here represents my problem but has been cut down to just illustrate the issue.
I mimicked your logic, and it runs fine. I replaced some of your code with copy-paste from Play documentation just to keep it minimal. I tested it on top of a setup I'm working on now
so you'll see artifacts foreign to the default Play setup. This setup is more or less identical to the one described in the article I linked originally. I wouldn't know how to get more direct than this:
In controller:
import play.api.data._
import play.api.data.Forms._
case class UserData(name: String, age: Int)
val userFormConstraints2 = Form(
mapping(
"name" -> nonEmptyText,
"age" -> number(min = 0, max = 100)
)(UserData.apply)(UserData.unapply)
)
def test = Action {
implicit request => {
userFormConstraints2.bindFromRequest().fold(
formWithErrors => BadRequest("bad"),
userData => {
Ok(userData.name + userData.age)
}
)
}
}
Test:
class TempSpec extends Specification with MyHelpers {
"1" can {
"direct access to controller while posting" in new TestServer {
// `in new TestServer` spawns dependencies (`server`)
val controller = new controllers.Kalva(server)
// I instantiate the controller passing the dependency
val request = FakeRequest(POST, "bla")
.withFormUrlEncodedBody(
"name" -> "Richard",
"age" -> "1"
)
val result = controller.test()(request)
status(result) must equalTo(OK)
contentAsString(result) must contain("Richard");
val request_bad = FakeRequest(POST, "bla")
.withFormUrlEncodedBody(
"name" -> "",
"age" -> "-1"
)
val result_bad = controller.test()(request_bad)
status(result_bad) must equalTo(400)
contentAsString(result_bad) must contain("bad");
}
}
}
Global.scala:
object Global extends GlobalSettings {
private lazy val injector = Guice.createInjector(new TestModule)
override def getControllerInstance[A](controller: Class[A]) = {
injector.getInstance(controller)
}
}
TestModule:
import com.google.inject._
import com.tzavellas.sse.guice.ScalaModule
class TestModule extends ScalaModule {
def configure() {
#Provides
def getServer:Server = {
...
}
}
}
Within routes file:
POST /bla #controllers.Kalva.test
// the `#` prefix is needed because of how we fetch controllers
Original answer below:
class TranslateSpec extends Specification {
"Translate" should {
// The normal Play! way
"accept a name, and return a proper greeting" in {
running(FakeApplication()) {
val translated = route(FakeRequest(GET, "/greet/Barney")).get
status(translated) must equalTo(OK)
contentType(translated) must beSome.which(_ == "text/html")
contentAsString(translated) must contain ("Barney")
}
}
// Providing a fake Global, to explitly mock out the injector
object FakeTranslatorGlobal extends play.api.GlobalSettings {
override def getControllerInstance[A](clazz: Class[A]) = {
new Translate(new FakeTranslator).asInstanceOf[A]
}
}
"accept a name, and return a proper greeting (explicitly mocking module)" in {
running(FakeApplication(withGlobal = Some(FakeTranslatorGlobal))) {
val home = route(FakeRequest(GET, "/greet/Barney")).get
contentAsString(home) must contain ("Hello Barney")
}
}
// Calling the controller directly, without creating a new FakeApplication
// (This is the fastest)
"accept a name, and return a proper greeting (controller directly, no FakeApplication!)" in {
val controller = new Translate(new FakeTranslator)
val result = controller.greet(name = "Barney")(FakeRequest())
contentAsString(result) must contain ("Hello Barney")
}
}
}
The code above is quite self-descriptive and it shows the default testing workflow and how it can be improved with Dependency Injection. It's a quote from this article.
This particular excerpt is from the "Why should I use DI with Play?" section. The article is about setting up Google Guice with Play2 and the possibilities it opens up. It's a practical read.
As you can see above, "The normal Play! way" is fine, but by embracing DI you can get away with so much more in your tests (and development in general of course).
As is described in the article, using Guice with Play involves making minor changes to Play's default set-up and it's well worth it. I've been doing it this way for a long time now, and haven't looked back.