Mockito with Dependency Injection - scala

I'm using Play Framework 2.5 and trying to test one of my DAO class based on the example here.
But my class under test has dependency injection and I wonder how to mock it.
I tried to do something but it seems a bit clumsy, and I would like help to improve it.
Here is my code :
class MyController #Inject()(val mydao: MyDAOClass) extends Controller {
def getData = Action { request =>
mydao.fetchData
}
}
class MyDAOClass #Inject()(ws: WSClient) {
def urls(): List[String] = { List("url1", "url2") }
// fetch data on different servers and should merge it.
def fetchData(): Future[List[String]] = {
val futures: List[Future[WSResponse]] = urls map { url =>
ws.url(url + "/some/data").withRequestTimeout(10000.millis).get()
}
futures map { future =>
future onComplete {
case Success(resp) => println(resp.json)
case Failure(t) => println("An error has occured: " + t.printStackTrace)
}
}
Future(List())
}
}
Here is my test code :
trait DAOTrait {
def urls: List[String]
}
class MyDAOClass extends PlaySpec with MockitoSugar {
"MyDAOClass" should {
"get a list of data" in {
Server.withRouter() {
case GET(p"/url1/some/data") => Action {
Results.Ok(Json.arr(Json.obj("name" -> "data1")))
}
} { implicit port =>
val mockMyDAOClass = mock[DAOTrait]
when(mockMyDAOClass.urls) thenReturn List("url1")
implicit val materializer = Play.current.materializer
WsTestClient.withClient { client =>
val mydao = new MyDAOClass(client) {
override def urls = mockMyDAOClass.urls
}
val result = Await.result(
mydao.fetchData(), 10.seconds)
result mustEqual List("data1")
}
}
}
}
}
The code is not finished in MyDAOClass, but it is enough to start implementing tests.
1) How to mock class that have dependency injection ?
2) When testing this code I have the following exception in MyDAOClass case Failure(t)
java.net.ConnectException: http://localhost:40183
at org.asynchttpclient.netty.channel.NettyConnectListener.onFailure(NettyConnectListener.java:137)
at org.asynchttpclient.netty.request.NettyChannelConnector$1.onFailure(NettyChannelConnector.java:106)
at org.asynchttpclient.netty.SimpleChannelFutureListener.operationComplete(SimpleChannelFutureListener.java:28)
at org.asynchttpclient.netty.SimpleChannelFutureListener.operationComplete(SimpleChannelFutureListener.java:20)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:683)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:564)
at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:425)
at io.netty.channel.nio.AbstractNioChannel.doClose(AbstractNioChannel.java:462)
at io.netty.channel.socket.nio.NioSocketChannel.doClose(NioSocketChannel.java:236)
at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:611)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:590)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:534)
at io.netty.channel.nio.NioEventLoop.closeAll(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:361)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.nio.channels.ClosedChannelException
but it should return Result.Ok instead.
3) Any advice welcome.

Related

How to refactor this scala code?

I want to refactor this code into something more readable and better in general. I know that in Scala there are normally neat ways of doing things but for me it's getting a bit messy (BTW I'm using the Play library in the code). this is a snippet of my code:
class HomeController #Inject()
(cc: ControllerComponents)
(implicit val config: Configuration)
extends AbstractController(cc) {
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {
implicit request: Request[String] =>
val promise = Promise[Result]()
Future {
val removeId = request.body.toLong
println(s"remove id $removeId")
promise completeWith {
idElementsDAO.remove(removeId, uid.toLong) map {
_ => Ok("")
} recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
BadRequest(errorMessage)
}
}
} recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
promise.success(BadRequest(errorMessage))
}
promise.future
}
}
Assuming that idElementsDAO.remove return a Future, this is probably more idiomatic:
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {implicit request =>
val removeId = request.body.toLong
println(s"remove id $removeId")
idElementsDAO.remove(removeId, uid.toLong)
.map(_ => NoContent) // probably more correct than `Ok("")`
.recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
BadRequest(errorMessage)
}
}
No need for the Promise or the call to Future {...} (Future.apply).
Keep in mind, it's probably not the best idea to directly pass the underlying error of any Throwable directly to the http client (browser?).
If you add generic error handling code to the global error handler (for unexpected errors) that logs the error and sends a generic message to the front-end, you can then write it even cleaner like this:
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {implicit request =>
val removeId = request.body.toLong
println(s"remove id $removeId")
for {
_ <- idElementsDAO.remove(removeId, uid.toLong)
} yield NoContent
}
https://www.playframework.com/documentation/2.6.x/ScalaErrorHandling
Here is a simpler version of your code:
class HomeController #Inject()(cc: ControllerComponents)(implicit val config: Configuration)
extends AbstractController(cc) {
def removeIdElement(uid: String) = HAction(uid, "AuthEvent", 1, "login", parse.text).async {
implicit request: Request[String] =>
Future {
val removeId = request.body.toLong
println(s"Removing id $removeId")
removeId
}.flatMap(id => idElementsDAO.remove(id, uid.toLong))
.map(_ => Ok(""))
.recover {
case t: Throwable =>
val errorMessage = getMessageFromThrowable(t)
println(s"Removing id element failed: ${errorMessage}")
BadRequest(errorMessage)
}
}
}
In the above code, a Promise is not needed, and the recover combinator is not repeated.

How can I serialize Sangria responses with json4s and Akka HTTP?

I'm working through a slight variation of Sangria's Getting Started, using Akka HTTP. I'm attempting to use json4s-jackson as the serializaltion lib, but am running in to some trouble getting the response I want.
Specifically, the serialized response I get is the JSON version of the (StatusCode, Node) tuple:
{
"_1": {
"allowsEntity": true,
"defaultMessage": "OK",
"intValue": 200,
"reason": "OK"
},
"_2": {
"data": {
"foo": {
"id": "1",
"name": "Foo"
}
}
}
}
The data portion is correct, but obviously I just want that and not the first element of the serialized tuple.
I'm using akka-http-json4s, so my trait with route looks like:
case class GraphQlData(query: String, operation: Option[String])
trait FooController {
import de.heikoseeberger.akkahttpjson4s.Json4sSupport._
implicit val serialization = jackson.Serialization
implicit val formats = DefaultFormats
val fooRoutes = post {
entity(as[GraphQlData]) { data =>
QueryParser.parse(data.query) match {
// query parsed successfully, time to execute it!
case Success(queryAst) =>
complete {
Executor
.execute(
SchemaDefinition.FooSchema,
queryAst,
new FooService,
operationName = data.operation
)
.map(OK -> _)
.recover {
case error: QueryAnalysisError => BadRequest -> error.resolveError
case error: ErrorWithResolver => InternalServerError -> error.resolveError
}
}
// can't parse GraphQL query, return error
case Failure(error) =>
complete(BadRequest -> error.getMessage)
}
}
}
implicit def executionContext: ExecutionContext
}
For the life of me I can't figure out what's wrong. I've been looking at sangria-akka-http-example but it seems to be exactly the same, with the exception of using spray-json instead of json4s.
Ideas? Thanks!
Ah, figured it out. I neglected to add
import sangria.marshalling.json4s.jackson._
to the trait defining the route. Adding it does the trick.
Just wanted to provide a quick update to this answer for the full GraphQLRequest. Now the variables are included in the request.
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import org.json4s._
import org.json4s.JsonAST.JObject
import sangria.marshalling.json4s.jackson._
case class GQLRequest(query: String, operationName: Option[String], variables: JObject)
trait SomeJsonSupport extends Json4sSupport {
implicit val serialization = jackson.Serialization
implicit val formats = DefaultFormats
}
trait GraphQLResource extends SomeJsonSupport{
implicit val timeout:Timeout
implicit val system:ActorSystem
import system.dispatcher
def graphqlRoute: Route =
(post & path("graphql")) {
entity(as[GQLRequest]) { requestJson =>
println(s"This is the requestJson = $requestJson")
graphQLEndpoint(requestJson)
}
} ~
get {
println(s"This is working")
getFromResource("graphiql.html")
}
def graphQLEndpoint(requestJson: GQLRequest): Route = {
val route = QueryParser.parse(requestJson.query) match {
case Success(query) =>
println(s"This is the query $query")
val vars = requestJson.variables match {
case jObj:JObject => jObj
case _ => JObject(List.empty)
}
val futureJValue = Executor.execute(clientSchema,
query,
NclhGqlRequest(this),
operationName = requestJson.operationName,
variables = vars)
val futureTupleStatusCodeJValue = futureJValue.map(OK -> _).recover {
case error: QueryAnalysisError => BadRequest -> error.resolveError
case error: ErrorWithResolver => InternalServerError -> error.resolveError
}
complete(futureTupleStatusCodeJValue)
case Failure(error) =>
complete(BadRequest, error.getMessage)
}
route
}

When a scala recover should be used:

I have a simple flow of orders from an controller to services back to the controller, and im trying to make sure I use future recovers in the right place and in general to cover exceptions properly.
controller action:
def getSiblings(): Action[JsValue] = Action.async(parse.json) { request =>
request.body.validate[Person] match {
case JsSuccess(person, _) =>
peopleService.getSiblings(person).map(res => Ok(Json.toJson(res))) recover {
case t: Throwable =>
logger.error("error running getSiblings: ", t)
InternalServerError
}
case JsError(errors) => Future(BadRequest(s"not a good person format ${errors.mkString}"))
}
}
peopleService:
class PeopleService #Inject() extends LazyLogging {
def getSiblings(personToGetSiblings: Person): Future[List[SiblingResults]] = {
// isSibling is a method of a person that returnes a future and can fail
Future.sequence(listOfPeople.map(person => person.isSibling(personToGetSiblings))) recover {
case e: Exception => {
throw new RuntimeException("fail to get siblings with error: ", e)
}
}
}
}
case class SiblingResults (person: Option[Person])
and a Person:
#Singleton
class PersonA #Inject() (configuration: Configuration, peopleApi: PeopleApi) extends Person {
def isSibling(personToMatch: Person): Future[SiblingResults] = {
val res = for {
// areSiblings returnes a Future[Boolean]
areThey <- peopleApi.areSiblings(personToMatch, personInstance) recover {
case ex: Exception => throw new Exception("PeopleApi failed")
}
} yield areThey
if (res) Some(personInstance) else None
}
val personInstance = this
...
}
what will be the right way to recover those futures?
Use Play's Action composition to handle any failures. This your code would be clean handling only the business logic without the extra plumbing stuff like exception handling etc. You let the exceptions bubble up to the controller and exception is handled finally by the ActionBuilder.
ActionBuilder
import play.api.libs.json.Json
import play.api.mvc.{ActionBuilder, Request, Result}
import play.api.mvc.Results.Status
import scala.concurrent.Future
/**
* Created by chlr on 12/2/16.
*/
object ErrRecoveryAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]) = {
block(request) recover errorHandler
}
def errorHandler: PartialFunction[Throwable, Result] = {
// you can apply a different statuscode other than 500 depending on the type of exception matched.
case th: Throwable =>
new Status(500).apply(Json.obj("error_message" -> th.getMessage, "error_class" -> th.getClass.getName))
}
}
Controller usage:
Note how there is no exception handling in the controller and furthermore no exception handling is required in other service classes.
def getSiblings(): Action[JsValue] = ErrRecoveryAction.async(parse.json) {
request =>
peopleService
.getSiblings(request.body.as[Person])
.map(res => Ok(Json.toJson(res)))
}

getting scheme java.lang.NullPointerException: scheme while trying to fake a server call in my service

I have a service class in my project and I want to test one of its methods that is performing an api call, so I want to catch this call and return something fake so I can test my method, it looks like this:
class MyService #Inject()(implicit config: Configuration, wsClient: WSClient) {
def methodToTest(list: List[String]): Future[Either[BadRequestResponse, Unit]] = {
wsClient.url(url).withHeaders(("Content-Type", "application/json")).post(write(list)).map { response =>
response.status match {
case Status.OK =>
Right(logger.debug("Everything is OK!"))
case Status.BAD_REQUEST =>
Left(parse(response.body).extract[BadRequestResponse])
case _ =>
val ex = new RuntimeException(s"Failed with status: ${response.status} body: ${response.body}")
logger.error(s"Service failed: ", ex)
throw ex
}
}
}
}
and now in my test class I go:
class MyServiceTest extends FreeSpec with ShouldMatchers with OneAppPerSuite with ScalaFutures with WsScalaTestClient {
implicit lazy val materializer: Materializer = app.materializer
lazy val config: Configuration = app.injector.instanceOf[Configuration]
lazy val myService = app.injector.instanceOf[MyService]
"My Service Tests" - {
"Should behave as im expecting" in {
Server.withRouter() {
case POST(p"/fake/api/in/conf") => Action { request =>
Results.Ok
}
} { implicit port =>
WsTestClient.withClient { implicit client =>
whenReady(myService.methodToTest(List("1","2","3"))) { res =>
res.isRight shouldBe true
}
}
}
}
}
}
and I get this error:
scheme java.lang.NullPointerException: scheme
also tried put under client => :
val myService = new MyService {
implicit val config: Configuration = configuration
implicit val ws: WSClient = client
}
but got some other error that I dont have enough arguments in the constructor...
why is it not working?
if there is a better nd simpler way to fake this api call i will love to hear it :)
thanks!
Server.withRouter may not be exactly what you want. It creates a server and bound it to a random port, per instance (unless you specify the port). It also creates its own instance of application which will be disconnected from the app you used to instantiate the service.
Another thing is that the injected WSClient do not works relative to your application. You need to use the client which is passed to WsTestClient.withClient block instead. So, you should do something like:
class MyServiceTest extends FreeSpec with ShouldMatchers with OneAppPerSuite with ScalaFutures with WsScalaTestClient {
implicit lazy val materializer: Materializer = app.materializer
lazy val config: Configuration = app.injector.instanceOf[Configuration]
"My Service Tests" - {
"Should behave as im expecting" in {
Server.withRouter() {
case POST(p"/fake/api/in/conf") => Action { request =>
Results.Ok
}
} { implicit port =>
WsTestClient.withClient { implicit client =>
// Use the client "instrumented" by Play. It will
// handle the relative aspect of the url.
val myService = new MyService(client, config)
whenReady(myService.methodToTest(List("1","2","3"))) { res =>
res.isRight shouldBe true
}
}
}
}
}
}

ScalaTest: Assert exceptions in failed futures (non-blocking)

import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.ScalaFutures
import org.apache.thrift.TApplicationException
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution {
it should "throw org.apache.thrift.TApplicationException for invalid Ids" in {
val future: Future[Response] = ThriftClient.thriftRequest
whenReady(future) {
res => {
intercept[TApplicationException] {
}
}
}
}
}
Question: How do you assert expected failures in Futures without blocking? The above doesn't work, the exception is thrown before the intercept block.
I know this is probably a bit late, but ScalaTest provides this feature out of the box (I believe since version 2) by mixing in the ScalaFutures trait, or using it directly in your test functions. Behold!
test("some test") {
val f: Future[Something] = someObject.giveMeAFuture
ScalaFutures.whenReady(f.failed) { e =>
e shouldBe a [SomeExceptionType]
}
}
Or you can perform some other assertions in there. Basically, if your future doesn't fail like you expect, the test will fail. If it fails, but throws a different exception, the test will fail. Nice and easy! =]
cheeky edit:
You can also use this method to test anything that returns a future:
test("some test") {
val f: Future[Something] = someObject.giveMeAFuture
ScalaFutures.whenReady(f) { s =>
// run assertions against the object returned in the future
}
}
Most recent edit!
I just wanted to update this answer with more useful information based on newer versions of Scala test. The various spec traits now all have async support, so instead of extending, say, WordSpec, you would instead extend AsyncWordSpec, and instead of relying on the whenReady calls as above, you would just map over your futures directly in the test.
Example:
class SomeSpec extends Async[*]Spec with Matchers {
...
test("some test") {
someObject.funcThatReturnsAFutureOfSomething map { something =>
// run assertions against the 'something' returned in the future
}
}
}
This was buried in a comment as well, but Scalatest's FutureValues mixin has you covered.
Just use f.failed.futureValue shouldBe an[TApplicationException]
Note: leaving this answer because the OP found it helpful, but for Scala Futures see the other answer.
This is a bit boilerplated, but Waiter from AsyncAssertions:
import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.{ ScalaFutures, AsyncAssertions, PatienceConfiguration }
import concurrent.Future
import concurrent.ExecutionContext.Implicits._
import util._
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with AsyncAssertions {
it should "throw for invalid Ids" in {
val f: Future[Int] = new Goof().goof
val w = new Waiter
f onComplete {
case Failure(e) => w(throw e); w.dismiss()
case Success(_) => w.dismiss()
}
intercept[UnsupportedOperationException] {
w.await
}
}
}
given
import concurrent.Future
import concurrent.ExecutionContext.Implicits._
class Goof {
def goof(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
throw new UnsupportedOperationException
}
def goofy(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
throw new NullPointerException
}
def foog(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
7
}
}
In other words,
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with AsyncAssertions {
it should "throw for invalid Ids" in {
val f: Future[Int] = new Goof().goof
import Helper._
f.failing[UnsupportedOperationException]
}
}
object Helper {
implicit class Failing[A](val f: Future[A]) extends Assertions with AsyncAssertions {
def failing[T <: Throwable](implicit m: Manifest[T]) = {
val w = new Waiter
f onComplete {
case Failure(e) => w(throw e); w.dismiss()
case Success(_) => w.dismiss()
}
intercept[T] {
w.await
}
}
}
}
Or, if you have multiple futures and you want the first non-conforming future to fail the test:
trait FailHelper extends Assertions with AsyncAssertions with PatienceConfiguration {
def failingWith[T <: Throwable : Manifest](fs: Future[_]*)(implicit p: PatienceConfig) {
val count = new java.util.concurrent.atomic.AtomicInteger(fs.size)
val w = new Waiter
for (f <- fs) f onComplete {
case Success(i) =>
w(intercept[T](i))
println(s"Bad success $i")
w.dismiss()
case Failure(e: T) =>
println(s"Failed $e OK, count ${count.get}")
w(intercept[T](throw e))
if (count.decrementAndGet == 0) w.dismiss()
case Failure(e) =>
println(s"Failed $e Bad")
w(intercept[T](throw e))
w.dismiss()
}
w.await()(p)
}
}
with usage
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with FailHelper {
it should "throw for invalid Ids" in {
val sut = new Goof()
import sut._
val patienceConfig = null // shadow the implicit
implicit val p = PatienceConfig(timeout = 10 seconds)
// all should fail this way
//failingWith[UnsupportedOperationException](goof(), goofy(3), foog(5))
//failingWith[UnsupportedOperationException](goof(), foog(5))
failingWith[UnsupportedOperationException](goof(), goof(2), goof(3))
}
}
Inspired by this unloved answer.
ScalaTest 3.0 adds async versions of the spec traits like AsyncFreeSpec:
import org.scalatest.{AsyncFlatSpec, Matchers}
import scala.concurrent.Future
class ScratchSpec extends AsyncFlatSpec with Matchers {
def thriftRequest = Future { throw new Exception() }
it should "throw exception" in {
recoverToSucceededIf[Exception] {
thriftRequest
}
}
}
Addition to Brian Low's answer, I found a nice explanation for recoverToSucceededIf. This is available in all Async styles (from ScalaTest 3):
Failed futures can be tested in two ways: using recoverToSucceededIf or recoverToExceptionIf
recoverToSucceededIf is used for asserting the type of the exception the future ends in:
"return UserNotFoundException" when {
"the user does not exist" in {
recoverToSucceededIf[UserNotFoundException](userService.findUser("1"))
}
}
recoverToExceptionIf is useful when you want to test some of the exception's fields:
"return UserAlreadyExistsException" when {
"adding a user with existing username" in {
recoverToExceptionIf[UserAlreadyExistsException] {
userService.addUser(user)
}.map { ex =>
ex.message shouldBe s"User with username: $username already exists!"
}
}
}
See the whole blog from Tudor Zgureanu
—
What's new in ScalaTest 3
You can also try this Something Simple and Short
test("some test throwing SQL Exception") {
val f: Future[Something] = someObject.giveMeAFuture
recoverToSucceededIf[SQLException](f)
}