Blog

JMock to Scalamock Cheat Sheet

Quick reference cheat sheet mapping JMock idioms to their Scalamock equivalents. Useful when migrating Java tests to Scala or learning Scalamock.

An abbreviated set of JMock examples with their Scalamock equivalents.

Mock Objects & the “Context”

private final Mockery context = new JUnit4Mockery();

private final ScheduledExecutorService executor = context.mock(ScheduledExecutorService.class);
private final ScheduledFuture future = context.mock(ScheduledFuture.class);
"A test with a mock context in scope" in new MockContext {
  val executor = mock[ScheduledExecutorService]  
  val future = mock[ScheduledFuture[Any]]
  // ...
}

Returns

// java
context.checking(new Expectations() {{ braces }}
    oneOf(executor).shutdownNow(); will(returnValue(asList(waiting)));
    oneOf(waiting).cancel(true);
}});
// scala
(executor.shutdownNow _).expects().returning(asList(waiting)).once
(waiting.cancel _).expects(true).once

Notes:

  • expects() is required for zero argument method call expectations.
  • You can leave off once; it will default to the same behaviour

Allowing / Ignoring

// java
context.checking(new Expectations() {{ braces }}
    allowing(executor).scheduleWithFixedDelay(with(any(Runnable.class)), with(any(Long.class)), with(any(Long.class)), with(any(TimeUnit.class))); will(returnValue(future));
    oneOf(future).cancel(true);
}});
// scala
(executor.scheduleWithFixedDelay _).expects(*, *, * , *).returning(future)
(future.cancel _).expects(true).once

Notes:

  • You could also add .anyNumberOfTimes after the returning call but it’s unnecessary.
  • There’s no way to distinguish the intention of allowing and ignoring interactions in Scalamock.

Default Values

JMock will return a default value (as a dynamic proxy) if you set up an expectation but leave off a returnValue. In the example below, we don’t care if it returns anything so if the code under test relies on a value, but the test does not, we don’t have to express anything in the test.

oneOf(factory).create();

If the underlying code were to check, say, that the result of factory.create() was not an empty list with if (result.isEmpty()), JMock would return something sensible and we’d avoid a NullPointerException. You might argue that this side affect should be captured in a test but leaving it off makes the intention of expectation clearer; we only care that create is called, not what it returns.

Scalamock will return null by default. So the above example would give a NullPointerException and you’re required to do something like this. Notice we’re using a stub and not a mock here.

val result = stub[Result]
(factory.create _).expects().once.returning(List(result))

Any / Wildcards

JMock uses with and Hamcrest the matcher IsAnything (any) to match anything. The type is used by the compiler.

context.checking(new Expectations() {{ braces }}
    ignoring(factory).notifyObservers(with(any(SomeException.class)));
    oneOf(factory).notifyObservers(with(any(AnotherException.class)));
}}

In the Scala version, use a type ascription to give the compiler a hand in the partially applied method call;

(factory.notifyObservers(_: SomeException)).expects(*).anyNumberOfTimes
(factory.notifyObservers(_: SomeException)).expects(*).once

Notes:

  • AnotherException is a subtype of SomeException but any will match on literally anything. Using subtypes like this in JMock is a bit of a smell as a test won’t fail if a different subtype is thrown at runtime. It may be useful to express intent.
  • You can’t replicate the subtype line in Scalamock; ((factory.notifyObservers(_: AnotherException)) doesn’t compile.

Throwing Exceptions

final Exception exception = new RuntimeException();
context.checking(new Expectations() {{ braces }}
    oneOf(factory).create(); will(throwException(exception));
    oneOf(factory).notifyObservers(exception);
}});
val exception = new Exception
(factory.create _).expects().throws(exception).once
(factory.notifyObservers(_: Exception)).expects(exception).once

Notes:

  • In Scalamock, throws and throwing are interchangeable.
  • Again, once is optional.
Discussion