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!
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.
Related
I am observing a strange behavior. In on of my test cases, I am using contentAsJson. In that test case, the compiler is not complaining that I have to provide an implicit value for Timeout and Materializer
class UserControllerUnitSpec extends PlaySpec with BeforeAndAfterAll with BeforeAndAfterEach with OneAppPerSuiteWithComponents{
..
"User signup request with body but with incorrect profile data " should {
"return error message " in {
...val resultFuture: Future[Result] = testEnv.controller.signupUser(request)
val responseBodyAsJsValue: JsValue = contentAsJson(resultFuture)//works
...
}
}
But in another test case, the compiler gives error that I need to provide the value
class QuestionsControllerUnitSpec extends PlaySpec with BeforeAndAfterAll with BeforeAndAfterEach with OneAppPerSuiteWithComponents{
...
"newQuestion" should {
"should return error if the size of the body in the request is more than the maximum allowed size" in {
...
val response:Accumulator[ByteString,Result] = questionController.newQuestion(request)
val responseBody = contentAsJson(response)//(Timeout(Duration(5000,"millis")),testEnv.testEnv.mat).
...
}
I get error
Error:(1485, 39) could not find implicit value for parameter mat: akka.stream.Materializer
val responseBody = contentAsJson(response)//(Timeout(Duration(5000,"millis")),testEnv.testEnv.mat)
How can I debug why one is working but the other isn't?
UPDATE - added return types after Mario's answer.
Try providing implicit Materializer like so
import play.api.test.Helpers._
implicit val actorSystem = ActorSystem("test")
implicit val materializer = ActorMaterializer()
val responseBody = contentAsJson(response)
instead of explicit testEnv.testEnv.mat
contentAsJson(response)(Timeout(Duration(5000,"millis")),testEnv.testEnv.mat)
Regarding the difference between the two tests, note there are two overloaded versions of contentAsJson
def contentAsJson(of: Future[Result])(implicit timeout: Timeout, mat: Materializer = NoMaterializer): JsValue
def contentAsJson(of: Accumulator[ByteString, Result])(implicit timeout: Timeout, mat: Materializer): JsValue
where in the first case we see default Materializer argument is provided
mat: Materializer = NoMaterializer
while in the second case we have to provided our own. Therefore it is likely that in the first test the type of resultFuture is Future[Result] whilst in the second test the return type of response is Accumulator.
Regarding finding out where the implicit is provided from, personally I use IntelliJ's View | Show Implicit Hints feature.
I have AkkaHttp client and cats library. I'd like to escape throwing exception, so I wrote this code:
class AkkaHttpJokeClient(url: String)(implicit system: ActorSystem) extends JokeClient[IO] {
override def getJoke(): IO[Either[Throwable, Joke]] = {
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val ec: ExecutionContext = system.dispatcher
IO.fromFuture(IO {
Http()
.singleRequest(HttpRequest(uri = url))
.flatMap(Unmarshal(_).to[String])
.map(x => Try{x.parseJson.convertTo[Joke]}.toEither)
})
}
}
It works, but I have a few problems with it. The first one is connection problem isn't solved. The second: I think it could be done easier, isn't it? I can't find right methods to do it better.
wireWith seems to have some issues with resolving implicit parameters.
Minimal example:
import com.softwaremill.macwire._
object A {
def props(x: Int)(implicit y: String): A = new A(x)
}
class A(x: Int)(implicit y: String) {
val sum: String = s"$x + $y"
}
object App {
def main(): Unit = {
val xVal: Int = 3
implicit val yVal: String = "5"
// val aInstance = wire[A] // works
// val aInstance = A.props(xVal) // works
val aInstance: A = wireWith(A.props _) // compile error
println(aInstance.sum)
}
}
App.main()
Error:
Error:(21, 33) type mismatch; found : Int required: String
val aInstance: A = wireWith(A.props _)
^
If implicit is ommitted at yVal, it complains about missing implicit:
Error:(18, 36) could not find implicit value for parameter y: String
val aInstance: A = wireWith(A.props _) // compile error
^
This is a simplified example though - in my production code i try to wire Actor props:
object Actor {
def props
(dependency: Dependency, refreshInterval: FiniteDuration ## CacheRefreshInterval)
(implicit ec: ExecutionContext): Props =
Props(new Actor(dependency, refreshInterval))
}
// DI container
protected implicit def executionContext: ExecutionContext
protected lazy val dependency: Dependency = wire[Dependency]
protected lazy val refreshInterval = 2.second.taggedWith[CacheRefreshInterval]
protected lazy val actorProps: Props ## ActorProps = actorwireWith(Actor.props _)
and get different compile error:
too many arguments for method props: (implicit ec: scala.concurrent.ExecutionContext)akka.actor.Props
I've tried making implicit parameter explicit and it worked just fine, except it's a bit against usual practice of passing execution context implicitly.
Am I doing something wrong or is it an issue in macwire?
Issue in Github: https://github.com/adamw/macwire/issues/125
If you were using wireWith(A.props _) as a workaround for macwire's inability to wire actors when their constructor are using implicit parameters (see macwire issue 121), you could switch to macwire 2.3.1 and wire your actors using wireActor[A], wireAnonymousActor[A] or wireProps[A]. issue 121 was fixed in macwire 2.3.1.
I am writing a simple word count flink job but I keep getting this error:
could not find implicit value for evidence parameter of type org.apache.flink.api.common.typeinfo.TypeInformation[String]
[error] .flatMap{_.toLowerCase.split("\\W+") filter {_.nonEmpty}}
I searched the net but could not get any comprehensible answer.
Here is my code:
object Job {
def main(args: Array[String]) {
// set up the execution environment
val env = StreamExecutionEnvironment.getExecutionEnvironment
val dataStream = env.readTextFile("file:///home/plivo/code/flink/scala/flinkstream/test/")
val count = dataStream
.flatMap{_.toLowerCase.split("\\W+") filter {_.nonEmpty}}
.map{ (_,1) }
.groupBy(0)
.sum(1)
dataStream.print()
env.execute("Flink Scala API Skeleton")
}
}
You have to import
import org.apache.flink.api.scala._
to enable implicit conversion instead of creating implicit value for each type that you use.
Adding this: implicit val typeInfo = TypeInformation.of(classOf[(String)]) as the first line in def main(args: Array[String]) {...} fixed it for me.
object Job {
def main(args: Array[String]) {
implicit val typeInfo = TypeInformation.of(classOf[(String)]) //Add this here
// set up the execution environment
val env = StreamExecutionEnvironment.getExecutionEnvironment
val dataStream = env.readTextFile("file:///home/plivo/code/flink/scala/flinkstream/test/")
val count = dataStream
.flatMap{_.toLowerCase.split("\\W+") filter {_.nonEmpty}}
.map{ (_,1) }
.groupBy(0)
.sum(1)
dataStream.print()
env.execute("Flink Scala API Skeleton")
}
}
I'm trying to write a simple HTTP client using Scala and spray-client. I'm basing my client on the examples given on Spray docs.
My issue is that the example is creating a new implicit ActorSystem i.e.
implicit val system = ActorSystem()
but I want my client to be reusable and not create a new ActorSystem.
Here's the gist of my code.
trait WebClient {
def get(url: String)(implicit system: ActorSystem): Future[String]
}
object SprayWebClient extends WebClient {
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
def get(url: String): Future[String] = {
val r = pipeline (Get("http://some.url/"))
r.map(_.entity.asString)
}
}
But I am getting two compiler errors regarding implicits
implicit ActorRefFactory required: if outside of an Actor you need an implicit ActorSystem, inside of an actor this should be the implicit ActorContext WebClient.scala ...
and
not enough arguments for method sendReceive: (implicit refFactory: akka.actor.ActorRefFactory, implicit executionContext: scala.concurrent.ExecutionContext, implicit futureTimeout: akka.util.Timeout)spray.http.HttpRequest => scala.concurrent.Future[spray.http.HttpResponse]. Unspecified value parameters refFactory, executionContext. WebClient.scala...
How should I change the API definitions?
Here's one solution:
import akka.actor.ActorSystem
import spray.http.{HttpRequest, HttpResponse}
import scala.concurrent.Future
import spray.client.pipelining._
trait WebClient {
def get(url: String): Future[String]
}
class SprayWebClient(implicit system: ActorSystem) extends WebClient {
import system.dispatcher
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
def get(url: String): Future[String] = {
val r = pipeline (Get("http://some.url/"))
r.map(_.entity.asString)
}
}
and here's another that keeps the original WebClient.get signature:
import akka.actor.ActorSystem
import spray.http.{HttpRequest, HttpResponse}
import scala.concurrent.Future
import spray.client.pipelining._
trait WebClient {
def get(url: String)(implicit system: ActorSystem): Future[String]
}
object SprayWebClient extends WebClient {
def get(url: String)(implicit system: ActorSystem): Future[String] = {
import system.dispatcher
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
val r = pipeline (Get("http://some.url/"))
r.map(_.entity.asString)
}
}
The second one is a bit more expensive because the pipeline is created anew every time even if it is theoretically static per ActorSystem. I would prefer the first solution and try to find a way to propagate the WebClient through your application (by using the cake pattern, by passing it around explicitly, or by using other dependency injection techniques).