Hot questions for Using Mockito in 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
- define additional beans source from MyControllerTest.MyConfig for Spring DI;
- in
mockExternalService
method manually create your bean-mock; - define this mock is primary implementation for ExternalService and...
- ...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