Working with Mocks

Mockito

Finatra recommends and integrates with the excellent Mockito Java mocking framework. As mentioned in the main Testing section, it is recommended that Scala users use the ScalaTest testing framework, however it is also recommended that users prefer Mockito Scala for Mockito mocking in Scala over the ScalaTest MockitoSugar utility (which provides only basic syntax sugar for Mockito).

c.t.util.mock.Mockito

c.t.util.mock.Mockito provides a Mockito Scala integration for Scala users of the framework. Java users are encouraged to use Mockito directly. The c.t.util.mock.Mockito will work as a full replacement of the ScalaTest org.scalatestplus.mockito.MockitoSugar trait.

The trait uses Mockito Scala Idiomatic Mockito which is heavily influenced by ScalaTest Matchers and provides a mocking DSL similar to the previous Specs2 Mockito integration.

Note

Usage of the Mockito Scala integration via c.t.util.mock.Mockito is optional. Users are free to choose ScalaTest MockitoSugar or another mocking library entirely for use in writing tests.

Using Mocks

When working with mocks it is strongly recommended that users choose to use Explicit Binding with #bind[T] to bind mocks to the object graph of a server or application under test. It is preferable to retain control over the lifecycle of a mock within a given test rather than binding a mock inside of a TwitterModule passed as an Override Module. Using the #bind[T] DSL allows for the test to retain control of the mock in order to expect or if necessary, reset, behavior.

To use, first add a dependency on util/util-mock and then mix in the c.t.util.mock.Mockito trait to your test. E.g.,

import com.twitter.finatra.http.EmbeddedHttpServer
import com.twitter.inject.server.FeatureTest
import com.twitter.util.mock.Mockito

class ExampleFeatureTest
  extends FeatureTest
  with Mockito {

  private val mockDownstreamServiceClient = mock[DownstreamServiceClient]
  private val mockIdService = mock[IdService]

  override val server =
    new EmbeddedHttpServer(new ExampleServer)
    .bind[DownstreamServiceClient].toInstance(mockDownstreamServiceClient)
    .bind[IdService].toInstance(mockIdService)

  test("service test") {
    /* Mock GET Request performed by DownstreamServiceClient */
    mockDownstreamServiceClient.get("/tweets/123.json")(manifest[FooResponse]) returns Future(None)
    ...
  }

You can then bind the mocked instance to the object graph of the server or application under test using the #bind[T] DSL and then expect behavior for the mocked instance as necessary.

This is preferable over creating a Module then binding the mock to the object graph in the module, e.g.,

import com.twitter.inject.TwitterModule
import com.twitter.util.mock.Mockito

// NOT RECOMMENDED
object MyTestModule extends TwitterModule with Mockito {
  override def configure(): Unit = {
    val mockFoo = mock[Foo]
    mockFoo.doSomething returns "Hello, world!"

    bind[Foo].toInstance(mockFoo)
  }
}

In the above example, the user of the module has no access to the mock object to be able to expect behavior of the mock (this is in essence a stub and you may very well find this useful).

You could change the code to instead expose passing of the mock object into the module,

import com.twitter.inject.TwitterModule

class MyTestModule(foo: Foo) extends TwitterModule {
  override def configure(): Unit = {
    bind[Foo].toInstance(foo)
  }
}

This would then allow a test to create the mock and retain control to be able to expect behavior. But at this point, the module does not provide much utility as this behavior can be more concisely done via the #bind[T] DSL.

Note

If you are trying to create reusable functionality and there is some ceremony that needs to be done on the mock and want to encapsulate the ceremony, then the above is a viable path. In practice, we’ve found this case to be rare and thus generally recommend using the #bind[T] DSL inside of a test when mocking.

Resetting Mocks

Warning

Note that the Mockito documentation frowns upon needing to reset mocks in a test and in most cases when testing a stateless service or application it is not expected that you would need to reset a mocked instance between test cases.

Generally, users set up a single server or application as a test member variable then test it against various cases. When doing so, mocks may be bound to the server or application’s object graph as in the example above and thus there may be occasion where the mocked instances need to be reset between test cases. However, if you are using a mock to capture the effects of state changes or transitions we recommend instead having the state changes emit Metrics and using stat assertions.

If you do need to reset mocks in test, it is recommended that you do so within an override of afterEach() if using Scala or similar functionality with an @After annotated method in JUnit if using Java. The Finatra framework mixes in the ScalaTest BeforeAndAfterEach trait, thus the ScalaTest afterEach() function is available to override. E.g.,

import com.twitter.finatra.http.EmbeddedHttpServer
import com.twitter.inject.server.FeatureTest
import com.twitter.util.mock.Mockito

class ExampleFeatureTest
  extends FeatureTest
  with Mockito {

  private val mockDownstreamServiceClient = mock[DownstreamServiceClient]
  private val mockIdService = mock[IdService]

  override val server =
    new EmbeddedHttpServer(new ExampleServer)
    .bind[DownstreamServiceClient].toInstance(mockDownstreamServiceClient)
    .bind[IdService].toInstance(mockIdService)

  override afterEach(): Unit = {
    // c.t.util.mock.Mockito provides a `reset(mocks*)` function
    reset(mockDownstreamServiceClient, mockIdService)
  }

  test("service test") {
    /* Mock GET Request performed by DownstreamServiceClient */
    mockDownstreamServiceClient.get("/tweets/123.json")(manifest[FooResponse]) returns Future(None)
    ...
  }

This will reset the mocks passed to the c.t.util.mock.Mockito.reset function after each test case has been run.

Note

In some cases you may want to reset a mock before each test case (because you do some pre-work with the mock in the constructor of the Test class). While it is generally more common to reset mocks after each test case, you an always do something similar before each test case by overriding the beforeEach() ScalaTest method or use an @Before annotated method with JUnit.

With Mockito Scala

Resetting mocks after each test is generally considered to be the preferred manner such that Mockito Scala provides specific ScalaTest helper traits to do so. Note that as documented, you should use the org.mockito.scalatest versions from mockito-scala-scalatest:

These traits will reset any mock instance created within the test, e.g., val f = mock[Foo] within a ScalaTest afterEach(). Any mock created differently, e.g., val f = MockitoSugar.mock[Foo] will not be reset since only the currently scoped mock methods are augmented to collect the mock instances for resetting.