In the deep trenches with JMockit Part 1
by Eing Ong
JMockit is a class loader remapping toolkit that you can use to replace your dependency classes by remapping them to your desired behavior when unit testing. Under the hood, JMockit uses Java instrumentation API to make direct byte code modifications at runtime.
JMockit framework abstracts this complex capability with its comprehsive APIs to allow engineers to mock interfaces, classes, enums, fields, methods, constructors, and accessors from public, private, protected, static, and final.
Finally, I’d like to point out that JMockit syntax is similar to a DSL (domain specific language) that users need to be familiar with.
I’ll be blogging the following important concepts in an order easier to learn and help answer questions. Hope this will help you too.
@Mocked
public class LoginServiceTest {
@Mocked UserAccount account;
@Mocked is the most commonly used annotation in JMockit. In the example above, UserAcount instance, “account” is annotated with Mocked. What does this really mean, i.e. What is mocked for UserAccount instance?
- All UserAccount methods (whether it is private/final/abstract/protected/static and constructors) and all its non-static initializers and non-static fields assignment (e.g. constructors)
- All of UserAccount super classes (if any) up to but not including java.lang.Object will be mocked recursively.
Tip: To mock static initializers, you have to use @Mocked(stubOutClassInitialization=true). By default, it is false. See Part 2 for an example.
Expectations
When we are unit testing a class, we want it to run reliably, repeatedly and fast. Typically, our class under test will depend on other classes/packages/jars that might be unreliable, expensive to setup/run, or are slow to response. Hence, we need a means to specify the behavior of these external APIs and JMockit provides Expectations to do just that.
@Mocked UserAccount account;
@Test
public void login_3passwordMismatches_shouldRevokeAccount() throws Exception
{
new Expectations() {
{
account.passwordMatches(anyString); result = false; minTimes = 3;
account.setRevoked(true);
}
};
for (int i = 0; i < 3; i++) {
service.login("john", "incorrect_password");
}
}
More examples using Expectations
Expectations examples | Return result(s) | What matches |
---|---|---|
Constructor new UserAccount(anyInt); minTimes = 1; maxTimes = 2; |
void | new UserAccount(1); new UserAccount(2); |
Method call account.passwordMatches(anyString); result = new Exception(); times = 1; |
Exception instance | @Mock or @Injectable account |
Mocked instance onInstance(account).passwordMatches(anyString); returns(false, true); |
1st call = false. 2nd call = true. |
@Mock or @Injectable account |
Different Types of Expectations
NonStrictExpectations | Expectations | StrictExpectations | |
---|---|---|---|
Checks order | No | Yes | Yes |
Checks invocation count | No 0 or more invocations unless “times=x” is added |
Yes At least 1 invocation |
Yes Exactly 1 invocation unless “times=x” is specified |
Use with Verifications | Definitely | Recommended | Not necessary |
Expectation Argument Matches
10 “any” argument/result matchers | 16 “with” argument matchers |
---|---|
any | withAny (T arg) |
anyBoolean | withArgThat(Matcher argument Matcher |
anyByte | withCapture(List |
anyChar | withEqual(double value, double delta) |
anyDouble | withEqual(float value, float delta) |
anyFloat | withEqual(T arg) |
anyInt | withInstanceLike(T object) |
anyLong | withInstanceOf(Class |
anyShort | withMatch(T regex) |
any String | withNotEqual(T arg) |
withNotNull() | |
withNull() | |
withPrefix(T text) | |
withSameInstance(T object) | |
withSubstring(T text) | |
withSuffix(T text) |
This concludes JMockit Part 1 and I’ll cover more topics in the next 2 parts of this JMockit blog -
Subscribe via RSS