3

I have a class A(httpClient: HttpExt)(implicit val system: ActorSystem, materializer: ActorMaterializer) that has a method called extractInfo (res: Future[HttpResponse]): Int that takes a future response and extracts an int value from the response entity.

I want to write a unit test for this method and I have the following in my unit spec class:

class ASpec extends UnitSpec with MockFactory {
  "An A" - {
    implicit val as = mock[ActorSystem]
    implicit val mat = mock[ActorMaterializer]
    val stubHttpClient = stub[HttpExt]
    val a = new A(stubHttpClient)

    "return auth token from response" in {
      val futureResponse: Future[HttpResponse] = <code that returns a Future[HttpResponse]>
      val info: Future[Option[Int]] = A.extractInfo(futureResponse)
      val result = Await.result(info, 10.seconds)
      result shouldBe Some(<a int value>)
    }
  }
}

However, on the line mock[ActorMaterializer], I got the following compilation error:

Error:(19, 28) object creation impossible, since:
it has 3 unimplemented members.
/** As seen from <$anon: akka.stream.ActorMaterializer>, the missing . 
signatures are as follows.
*  For convenience, these are usable as stub implementations.
*/
private[package akka] def actorOf(context: akka.stream.MaterializationContext,props: akka.actor.Props): akka.actor.ActorRef = ???
private[package akka] def logger: akka.event.LoggingAdapter = ???
private[package akka] def supervisor: akka.actor.ActorRef = ???
implicit val mat = mock[ActorMaterializer]

I would love some suggestions on how to solve this problem. Thank you enormously in advance!

Isa
  • 353
  • 2
  • 18
  • You can manually construct `HttpResponse` object and pass to `A`. This is better since it is a unit test of `A`, not a test of the endpoint. – simpadjo Sep 29 '17 at 06:25
  • @simpadjo do you mean I do something like `A.extractInfo()`? I tried this but it gives the same result :( – Isa Sep 29 '17 at 13:13
  • @simpadjo I also tried `stub[A]` but that does not allow me to call `A.extractInfo(..)` without setting expectations like `(a.extractInfo _).when(*).returns(...)`. I also tried putting `extractInfo` in a separate object, but the function needs a `ActorMaterializer` so I can not move it out of the original class. Do you know if there is a way to get around this? – Isa Sep 29 '17 at 13:22
  • 1
    You need too many mocks and stubs, it means that the code is too coupled. As I can guess `extractInfo` method doesn't really need a http client. So it should be placed in a class that doesn't depend on http client. So you can get rid of all your mocks. – simpadjo Sep 29 '17 at 14:30
  • @simpadjo Yea that is probably one way of doing it. Thank you :) – Isa Sep 29 '17 at 14:37

1 Answers1

0

I would suggest not mocking the ActorMaterializer but instead using a default implementation for example ActorMaterializer() or the one you use for your production code. At least from your example it doesn't look that you are making assertions on the ActorMaterializer itself.

cio
  • 58
  • 3
  • If I do that, I will get an `java.lang.IllegalArgumentException: ActorRefFactory context must be an ActorSystem or ActorContext`, and I would prefer not doing it the same way as I did with production code since this is just a unit test rather than an integration test. Is there way to get around this? Like I could `stub[A]` but that does not allow me to call `A.extractInfo(..)` without setting expectations like `(a.extractInfo _).when(*).returns(...)`. – Isa Sep 29 '17 at 13:17
  • Like I said I would suggest bringing up `ActorSystem("name")` and `ActorMaterializer()` in the ordinary way. Those `mock` methods are not really different (if they would work), they try to guess how the classes should be initiated and fail in this case. Unless you really need to mock certain behavior or the classes are complicated to instantiate explicitly, there is not much reason to use mocks. After all the HttpClient probably needs a working ActorSytem / ExecutionContext. – cio Sep 29 '17 at 13:40
  • Ok. How about `HttpExt`, is there a way I can create one of that and pass it to my `A`? If I try to `stub[HttpExt]`, it throws `NullPointerException` for some reason... – Isa Sep 29 '17 at 13:48
  • Possibly (I cannot see what the A does with the HttpClient). But I guess you have to also mock some behavior on that stub. – cio Sep 29 '17 at 13:55
  • `A` calls HttpClient's singleRequest so I want to add to my test `(stubHttpClient.singleRequest _) .when(*, *, *, *) .returns(Future(HttpResponse(...)))`. Is this a valid way of doing it? It still for some reason throws a `NullPointerException` on `stub[HttpExt]`.. – Isa Sep 29 '17 at 14:20