Hot questions for Using Mockito in playframework

Top 10 Java Open Source / Mockito / playframework

Question:

I am trying to write some functional tests, and I want to mock a service that consumes an external provider. But I can not set up the mock for the functions that return EitherT

This is the Trait whose implementation call the external service

@ImplementedBy(classOf[ExternalServiceImpl])
trait ExternalService {
  def index: EitherT[Future, String, JsValue]
}

In the CustomAppPerSuite trait I set up

val mockExternalService = mock[ExternalService]

 implicit override lazy val app = new GuiceApplicationBuilder()
.in(Mode.Test)
.overrides(bind[ExternalService].toInstance(mockExternalService))
.build()

val externalService = app.injector.instanceOf[ExternalService]

Then When I try to mock the succesful response

  "ExternalController#index" should {

    "return index data" in {
      doReturn(EitherT.rightT(Json.parse("""{"key": "value"}""")).when(externalService).index
      val fakeRequest = FakeRequest(GET, "/api/external")
      val result = externalController.index().apply(fakeRequest)
      status(result) mustBe OK
    }

But I get this error

[error]  found   : cats.data.EitherT[cats.package.Id,Nothing,JsValue]
[error]  required: cats.data.EitherT[scala.concurrent.Future,String,JsValue]
[error]   def index = EitherT.rightT(

I only want to mock the successful response, because it is what I am testing. Is there a way to do this?


Answer:

With mockito-scala-cats you could write that in a much more succint way

Json.parse("""{"key": "value"}""") willBe returnedF by externalService.index
//or
externalService.index shouldReturnF Json.parse("""{"key": "value"}""")

The library will look at the return type of externalService.index and get the appropiate cats.Applicative(s) to make this work smoothly.

Another advantage if you're running on Scalatest is that you can mix-in ResetMocksAfterEachTest and get all the mocks you wired into the play fake app to be automatically reset before each test.

Check here for more details

Question:

I'm writing a unit test suite for a Scala Play application and I'm wondering if there's anything analogous to java's

@Mock
private Foo foo;

@Autowired/InjectMocks
private Bar fixture;

@BeforeMethod
public void setUp() {
  MockitoAnnotations.initMocks(this);
}

For auto-mocking the annotated class and resetting it after every test

Currently I'm settling for

TestClass extends PlaySpec with BeforeAndAfterEach

private val foo = mock[Foo]

override def fakeApplication(): Application = 
  new GuiceApplicationBuilder().overrides(bind[Foo].toInstance(foo)).build

override protected def beforeEach(): Unit = {
  reset(foo)
}

A cursory attempt at using the java annotations in the scala test was not successful. My current approach works, I just want to make sure there's not a nicer one.


Answer:

mockito-scala solves this problem from version 0.1.1, as it provides a trait (org.mockito.integrations.scalatest.ResetMocksAfterEachTest) that helps to automatically reset any existent mock after each test is run

The trait has to be mixed after org.mockito.MockitoSugar in order to work, otherwise your test will not compile

So your code would look like this

TestClass extends PlaySpec with MockitoSugar with ResetMocksAfterEachTest

private val foo = mock[Foo]

override def fakeApplication(): Application = 
    new GuiceApplicationBuilder().overrides(bind[Foo].toInstance(foo)).build

The main advantage being not having to remember to manually reset each one of the mocks...

If for some reason you want to have a mock that is not reset automatically while using this trait, then it should be created via the companion object of org.mockito.MockitoSugar so is not tracked by this mechanism

Disclaimer: I'm a developer of that library

Question:

I'm using Play Framework 2.3.x, and I'd like to test that a call to a specific route such as "/" (its router is using several nested @Inject dependencies) ends by calling a specific method on a injected component.

For instance, a classic controller :

public class MyController extends Controller {
  @Inject private MyService myService;

  public Result index() { myService.foo(); }
  ...

 

The service impl injects another service that I want to mock:

@Service
public class MyServiceImpl implements MyService {
  @Inject private ExternalService externalService;

  public void foo() { externalService.call(...); }
  ...

I'd like to mock call() and retrieve its args to check if they contains the expected things.

@RunWith(SpringJUnit4ClassRunner.class)
@Profile("test")
@ContextConfiguration(classes = ApiConfiguration.class)
public class MyControllerTest {
  @Test public void test() {
    Result result = routeAndCall(new FakeRequest("GET", "/"), 10000);
    // here, I expect to retrieve and test the args of a mocked externalService.call
  }
}

I'm calling the route with FakeRequest (and do not inject the controller and calling manually the method) for some annotations to be taken into account and to have a http context (used in some area).

I'm using Mockito, I've tried several combinaison but couldn't inject my mock (the real method was always called), such as :

@Before public void initMocks() {
   MockitoAnnotations.initMocks(this);
}

@Mock ExternalService externalService;
...
when(externalService.call().then(invocation -> {
  Object[] args = invocation.getArguments();

Is it possible? Do you have an idea?

I've stumbled upon https://github.com/sgri/spring-reinject/ which seems to fit (didn't tested) but I'd like to not use another project for something I feel can be done without.

Thanks.


Answer:

The problem reason your DI injector doesn't know anything about this your mock

@Mock ExternalService externalService;

Spring context bean set and Mockito mock set initially doesn't have any intersection.

To fix this you should define mock as part of your Spring configuration. E.g. like this

@RunWith(SpringJUnit4ClassRunner.class)
@Profile("test")
@ContextConfiguration(classes = {ApiConfiguration.class, MyControllerTest.MyConfig.class})
public class MyControllerTest {

 @Autowired 
 ExternalService externalService;

  @Test public void test() {
     ...
  }

  @Configuration
  public static class MyConfig {
     @Bean 
     @Primary // it tells Spring DI to select your mock instead of your real ExternalService impl
     public ExternalService mockExternalService() {
         return Mockito.mock(ExternalService.class);
     }     
  }
}

With this code you

  1. define additional beans source from MyControllerTest.MyConfig for Spring DI;
  2. in mockExternalService method manually create your bean-mock;
  3. define this mock is primary implementation for ExternalService and...
  4. ...lets Spring to know about your bean-mock and autowire the mock anywhere in your system.

After this

@Autowired 
ExternalService externalService;

you can work with the mock in your tests as usual. E.g. define this behavior

Mockito.doThrow(NullPointerException.class).when(externalService).call(...);

Question:

I have the folliwng PlaySpec:

"Service A" must {

   "do the following" in {
       val mockServiceA = mock[ServiceA]
       val mockServiceB = mock[ServiceB]
       when(mockServiceA.applyRewrite(any[ClassA])).thenReturn(resultA) // case A
       when(mockServiceB.execute(any[ClassA])).thenReturn(Future{resultB})

       // test code continuation
   }
} 

The definition of ServiveA and ServiceB are

class ServiceA {
    def applyRewrite(instance: ClassA):ClassA = ???
}

class ServiceB {
   def execute(instance: ClassA, limit: Option[Int] = Some(3)) = ???
}

Mocking ServiceA#applyRewrite works perfectly. Mocking ServiceB#execute fails with the following exception:

Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at RandomServiceSpec.$anonfun$new$12(RandomServiceSpec.scala:146)

This exception may occur if matchers are combined with raw values:
    //incorrect:
    someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
    //correct:
    someMethod(anyObject(), eq("String by matcher"));

Although the instructions included in the exception seem a bit counterintuitive to me I have tried the following:

when(mockServiceB.execute(anyObject[ClassA])).thenReturn(Future{resultB})
when(mockServiceB.execute(anyObject())).thenReturn(Future{resultB})
when(mockServiceB.execute(anyObject)).thenReturn(Future{resultB})
when(mockServiceB.execute(any)).thenReturn(Future{resultB})
when(mockServiceB.execute(any, Some(3))).thenReturn(Future{resultB})
when(mockServiceB.execute(any[ClassA], Some(3))).thenReturn(Future{resultB})

All unfortunately to no avail. The only thing that changes is the number of expected and recorded matchers the exception refers to.

The weirdest thing for me though is that the mocking works perfectly for case A.


Answer:

Use the idiomatic syntax of mockito-scala and all the stuff related to the default argument will be deal with by the framework

mockServiceB.execute(*) returns Future.sucessful(resultB)

if you add the cats integration it could reduce to just

mockServiceB.execute(*) returnsF resultB

more info here