I am trying to run this test case in scala using mockito but i see the following error. can anyone suggest to me what am I doing wrong?
This is the Test case that I am running
Test case
class MailerServiceSpec extends BaseSpec {
val http: HttpExt = mock[HttpExt]
val mailService: NotificationService = mock[NotificationService]
val rawJobResponse: JsObject = Json.obj(("aman","verma"))
"Send Emails to multiple/single user" should {
"send email to user" in {
when(http.singleRequest(any[HttpRequest], any[HttpsConnectionContext], any[ConnectionPoolSettings], any[LoggingAdapter])) thenReturn
future(HttpResponse(status = StatusCodes.OK, entity = httpEntity(rawJobResponse)))
whenReady(mailService.sendMessage(any[String],any[String],any[String])(any[ExecutionContext])){ result =>
assert(result == Done)
}
}
}
}
This is the sendMessage method of mailerService that I want to test.
NotificationService.scala
override def sendMessage(receivers: List[String], subject: String, content: String)(implicit
ec: ExecutionContext
): Future[Done] = {
val notification = Notification(
recipients = receivers,
cc = Nil,
bcc = Nil,
sender = sender,
htmlBody = content,
htmlTitle = subject
)
val httpHeader = RawHeader("SENDGRID_API_KEY", sendgridApiKey)
(for {
entity <- Marshal(notification).to[MessageEntity]
_ <- makeHttpRequest(
HttpRequest(POST, notificationEmailUrl, entity = entity).addHeader(httpHeader)
)
} yield Done).andThen {
case Failure(exception) => logger.error("Failed to send email", exception)
}
}
Error
You have a NullPointerException here:
-> at org.scalatest.concurrent.ScalaFutures$$anon$1.futureValueImpl(ScalaFutures.scala:294)
because this method call was *not* stubbed correctly:
-> at scala.Option.orElse(Option.scala:477)
notificationService.sendMessage(
null,
null,
null,
null
);
You are calling notificationService in your test but it's a mock and you didn't define its behaviour.
I think you don't want to mock it but build a real NotificationService, otherwise you're just testing nothing.
Related
Currently trying to test authentication controller using specs2 and scalatest, my application runs fine, but the test always return NullPointerException or Scala Match error null
val userRepository: UserRepository = mock[UserRepository]
val userService: UserService = mock[UserService]
val authenticator: Authenticator = mock[Authenticator]
val authenticationService: AuthenticationService = mock[AuthenticationService]
implicit val ec = mock[ExecutionContext]
implicit val materializer = mock[Materializer]
val authenticationController = new AuthenticationController(Helpers.stubControllerComponents(
playBodyParsers = Helpers.stubPlayBodyParsers(materializer)
), authenticationService, authenticator, userService)
"User" should "login successfully" in {
val request = FakeRequest(POST, "/api/login").withHeaders(CONTENT_TYPE -> "application/json")
.withBody[JsValue](Json.parse("""{"email": "nghia_hd#flinters.vn", "password": "a12306789H#"}"""))
val result: Future[Result] = authenticationController.login().apply(request)
result.map {data => data.header.status shouldBe OK}
}
}
When using Intellij debugger, the exception seems to be here, but I dont really understand how to fix it
def login: Action[JsValue] = Action(parse.json) {
implicit request =>
UserLoginForm.form.bindFromRequest.fold(
formWithErrors => badRequestWarning(formWithErrors.errors),
user => {
--> authenticationService.verify(user) match {
case Success(result) => success(UserPayload.encode(result))
case Failure(exception) => exception match {
case _: PasswordNotMatch => error(Unauthorized, 401, "Password not match")
case _: EntityNotFoundException => error(Unauthorized, 400, "Email not found")
case _ => error(InternalServerError, 500, "An error occurred")
}
}
}
)
}
I've not used specs2/scalatest specifically, but usually you need to specify the response of the mocked method. You need to add this to the test:
when(authenticationService.verify(any())).thenReturn(someMockUser)
Otherwise (depending on your test settings) it will return null. There's also testing frameworks where you can instead throw an exception if methods that aren't mocked are called on mock objects.
I am using akka-http and trying to log a request on a specific path using logrequest :
path(Segment / "account") { id =>
logRequest("users/account", Logging.InfoLevel) {
post {
entity(as[Account]) { account => ???
complete(HttpResponse(StatusCodes.NoContent))
}
}
}
however on my log I see something like
HttpRequest(HttpMethod(POST),https://localhost:9009/api/users/123/account,List(Host: localhost:9009, User-Agent: akka-http/10.0.6, Timeout-Access: <function1>),HttpEntity.Chunked(application/json),HttpProtocol(HTTP/1.1))
what I am looking for is the exact request including the body (json) as it was sent by the requestor.
The "HttpEntity.Chunked(application/json)" segment of the log is the output of HttpEntity.Chunked#toString. To get the entire request body, which is implemented as a stream, you need to call HttpEntity#toStrict to convert the Chunked request entity into a Strict request entity. You can make this call in a custom route:
def logRequestEntity(route: Route, level: LogLevel)
(implicit m: Materializer, ex: ExecutionContext) = {
def requestEntityLoggingFunction(loggingAdapter: LoggingAdapter)(req: HttpRequest): Unit = {
val timeout = 900.millis
val bodyAsBytes: Future[ByteString] = req.entity.toStrict(timeout).map(_.data)
val bodyAsString: Future[String] = bodyAsBytes.map(_.utf8String)
bodyAsString.onComplete {
case Success(body) =>
val logMsg = s"$req\nRequest body: $body"
loggingAdapter.log(level, logMsg)
case Failure(t) =>
val logMsg = s"Failed to get the body for: $req"
loggingAdapter.error(t, logMsg)
}
}
DebuggingDirectives.logRequest(LoggingMagnet(requestEntityLoggingFunction(_)))(route)
}
To use the above, pass your route to it:
val loggedRoute = logRequestEntity(route, Logging.InfoLevel)
I am using web sockets with Play Framework in Scala. I would like to use Try/Catch functionality in my project for catching some Exceptions like Server Exception, Network Exception and etc.
What I did :
WebSocketController.scala
object LoginWS {
def props(out: ActorRef) = Props(new LoginWS(out))
}
class LoginWS(out: ActorRef) extends Actor {
def receive = {
case json_req: JsObject =>
var user_name = (json_req \ "user_name").as[String]
var password = (json_req \ "password").as[String]
var source = (json_req \ "source_type").as[String]
var result = UserLogin.authenticateUser(user_name, password).isDefined
var userID: Int = 0;
if(result) {
userID = UserLogin.getUserRole(user_name, password)
val login_status : String = "Success"
out ! Json.toJson(JsObject(Seq("login_status" -> JsString(login_status), "user_id" -> JsNumber(userID))))
}
else {
val login_status : String = "Failure"
out ! Json.toJson(JsObject(Seq("login_status" -> JsString(login_status), "user_id" -> JsNumber(userID))))
}
}
}
object WebSocketController extends Controller {
def login = WebSocket.acceptWithActor[JsValue, JsValue] { request =>
out => LoginWS.props(out)
}
}
What I tried :
I have used this answer posted by Ende Neu but it shows not found: value APIAction. Note: I added APIAction in routes file too
Code :
class LoginWS(out: ActorRef) extends Actor {
def receive = APIAction { request
case json_req: JsObject =>
.....
....//code here
}
}
object WebSocketController extends Controller {
def login = WebSocket.acceptWithActor[JsValue, JsValue] { request =>
out => LoginWS.props(out)
}
def APIAction(f: Request[AnyContent] => Result): Action[AnyContent] =
Action { request =>
Try(f(request))
.getOrElse(
InternalServerError(Json.obj("code" -> "500", "message" -> "Server error"))
)
}
}
Please help me to implement Try/Catch functionality in Web socket
Login request is handled within actor and I think you should handle your errors there. If you want to catch all exceptions instead of explicitly handling what could go wrong, I suggest doing the following
object SomeUtils {
def catchAll[A](out: ActorRef)(f: => A): Unit = {
val message = Try(f).getOrElse(Json.obj("code" -> "500", "message" -> "Server error"))
out ! message
}
}
import SomeUtils._
class LoginWS(out: ActorRef) extends Actor {
def receive = {
case json_req: JsObject => catchAll(out) {
val userName = (json_req \ "user_name").as[String]
val password = (json_req \ "password").as[String]
val authenticated = UserLogin.authenticateUser(userName, password).isDefined
if (authenticated) {
val role = UserLogin.getUserRole(userName, password)
Json.obj("login_status" -> "Success", "result" -> role)
}
else {
Json.obj("login_status" -> "Failure", "result" -> 0)
}
}
}
}
When handling jsonReq use catchAll method that would expect to get a receiver of the result and this result that could throw an Exception. In case of Exception it would use a default message with internal server error, and send this message to the receiver.
You could also make the out parameter implicit to skip putting it everywhere. Also your code is not really in scala style. Using vars etc...
If json sent to WS couldn't be parsed it would throw exception before reaching your code, solution can be found here:
How do I catch json parse error when using acceptWithActor?
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'm testing how a new Actor I'm working on handles unexpected messages. I'd like to assert that it throws a GibberishException in these cases. Here's the test and the implementation so far:
Test:
"""throw a GibberishException for unrecognized messages""" in {
//define a service that creates gibberish-speaking repositories
val stubs = new svcStub(
actorOf(new Actor{
def receive = { case _ => {
self.channel ! "you're savage with the cabbage"
}
}
})
)
val model = actorOf(new HomeModel(stubs.svc,stubs.store))
val supervisor = Supervisor(
SupervisorConfig(
OneForOneStrategy(List(classOf[Exception]), 3, 1000),
Supervise(model,Permanent) :: Nil
)
)
try{
intercept[GibberishException] {
supervisor.start
model !! "plan"
}
} finally {
supervisor.shutdown
}
stubs.store.plan should equal (null)
stubs.svcIsOpen should be (false)
}
Implementation:
class HomeModel(service: PlanService, store: HomeStore)
extends Actor {
private val loaderRepo = service.getRepo()
private var view: Channel[Any] = null
override def postStop() = {
service.close()
}
def receive = {
case "plan" => {
view=self.channel
loaderRepo ! LoadRequest()
}
case p: Plan => {
store.plan=p
view ! store.plan
}
case _ => throw new GibberishException(_)
}
}
However, when I run the test, the exception details get to the Supervisor I established, but I don't know how to do anything with them (like log them or test their type). I'd like to be able to get the exception details here from the supervisor so i can rethrow and intercept them in my test. Outside of a test method, I could imagine this being useful if you wanted to report the nature of an exception in the UI of a running app. Is there a way to get this from the Supervisor when it happens?
Change the OneForOneStrategy to only handle GibberishException, should solve it.