Mocking services in a test

How do you mock a Service Locator and its dependent Services in tests?

Are there any examples? I need it as an alternative to integration tests - https://www.lagomframework.com/documentation/1.4.x/scala/TestingServices.html#How-to-test-several-services

I have tried the documentation based solution - https://www.lagomframework.com/documentation/1.4.x/scala/TestingServices.html#How-to-test-one-service and the userService in WalletServiceImpl is == null.

The code:

class WalletServiceTest extends AsyncWordSpec with Matchers with Logable {

  val DEPOSIT_VALUE = 100

  val userClient: UserServiceApi = new UserServiceApi {
    override def register: ServiceCall[RegisterUserApi, UserRegisteredApi] = ???
    override def authenticate
      : ServiceCall[AuthenticateUserApi, UserAuthenticatedApi] = ???
    override def userByToken: ServiceCall[String, UserApi] = ???
    override def userById: ServiceCall[String, UserApi] = ???
    override def authenticateByFacebook
      : ServiceCall[AuthenticateUserByFacebookApi, UserEvent] = ???
    override def authenticatedFromService[Request, Response](
        serviceCall: UserApi => ServerServiceCall[Request, Response])(
        implicit ec: ExecutionContext): ServerServiceCall[Request, Response] = {
      info("AUTHENTICATED FROM SERVICE " * 1000)
      super.authenticatedFromService(serviceCall)(ec)
    }
  }

  val client: WalletServiceApi = {
    lazy val server = ServiceTest.startServer(ServiceTest.defaultSetup) { ctx =>
      new WalletApp(ctx) with LocalServiceLocator {
        override val userService: UserServiceApi = userClient
      }
    }

    server.serviceClient.implement[WalletServiceApi]
  }

  "Wallet service" should {

    s"accept deposit" in {
      val moneyAmount = MoneyAmount(DEPOSIT_VALUE)
      val makeDepositUi = MakeDepositUI(moneyAmount)
      for {
        depositMade <- client.makeDeposit
          .handleRequestHeader(rh =>
            rh.withHeader(UserServiceApi.HEADER_TOKEN, "user-001"))
          .invoke(makeDepositUi)
      } yield {
        trace(s"Deposit made: $depositMade")
        assert(depositMade.diffMoneyAmount.usd == DEPOSIT_VALUE)
      }
    }
  }
}

There’s no way that the overridden userService can be null in this code, so it must not be injected into WallerServiceImpl correctly. Can you post the code for WalletApp and WalletServiceImpl, and the stack trace you get when you run the test?

It’s also possible that there’s an initialization order issue that I’m not spotting. See https://docs.scala-lang.org/tutorials/FAQ/initialization-order.html for tips.

Thanks Tim! It was order related. I was confused by this exception (which still occurs):

2018-06-04 14:36:20,243 ERROR akka.actor.OneForOneStrategy - akka://application/user/KafkaBackoffConsumer1-spinning_bottle_events/KafkaConsumerActor1-spinning_bottle_events: exception during creation
akka.actor.ActorInitializationException: akka://application/user/KafkaBackoffConsumer1-spinning_bottle_events/KafkaConsumerActor1-spinning_bottle_events: exception during creation
	at akka.actor.ActorInitializationException$.apply(Actor.scala:193)
	at akka.actor.ActorCell.create(ActorCell.scala:671)
	at akka.actor.ActorCell.invokeAll$1(ActorCell.scala:525)
	at akka.actor.ActorCell.systemInvoke(ActorCell.scala:547)
	at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:282)
	at akka.dispatch.Mailbox.run(Mailbox.scala:223)
	at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
	at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
	at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
	at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
	at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: java.lang.NullPointerException: null
	at com.lightbend.lagom.scaladsl.server.LocalServiceLocator$$anon$1.getUri(LagomApplicationLoader.scala:318)
	at com.lightbend.lagom.scaladsl.server.LocalServiceLocator$$anon$1.locate(LagomApplicationLoader.scala:321)
	at com.lightbend.lagom.scaladsl.api.ServiceLocator.locateAll(ServiceLocator.scala:59)
	at com.lightbend.lagom.scaladsl.api.ServiceLocator.locateAll$(ServiceLocator.scala:58)
	at com.lightbend.lagom.scaladsl.client.CircuitBreakingServiceLocator.locateAll(ServiceLocators.scala:28)
	at com.lightbend.lagom.scaladsl.api.ServiceLocator.locateAll(ServiceLocator.scala:39)
	at com.lightbend.lagom.scaladsl.api.ServiceLocator.locateAll$(ServiceLocator.scala:39)
	at com.lightbend.lagom.scaladsl.client.CircuitBreakingServiceLocator.locateAll(ServiceLocators.scala:28)
	at com.lightbend.lagom.internal.scaladsl.broker.kafka.ScaladslKafkaSubscriber.$anonfun$atLeastOnce$1(ScaladslKafkaSubscriber.scala:128)
	at com.lightbend.lagom.internal.broker.kafka.KafkaSubscriberActor.preStart(KafkaSubscriberActor.scala:35)
	at akka.actor.Actor.aroundPreStart(Actor.scala:528)
	at akka.actor.Actor.aroundPreStart$(Actor.scala:528)
	at com.lightbend.lagom.internal.broker.kafka.KafkaSubscriberActor.aroundPreStart(KafkaSubscriberActor.scala:21)
	at akka.actor.ActorCell.create(ActorCell.scala:654)
	... 9 common frames omitted

Do you know what is the issue?