How TestableMock Empowers Java Unit Testing of Private Members and Void Methods
TestableMock is a Java unit‑testing helper that lets developers access private fields and methods, quickly mock any method call, and effectively test void methods by providing annotations and utility classes, with detailed code examples and usage guidelines for both Java and Kotlin projects.
TestableMock is a Java unit‑testing helper built on source‑code and bytecode enhancement, offering three main capabilities: accessing private members of the class under test, quickly mocking arbitrary method calls, and assisting testing of void methods.
Accessing private fields and methods
Developers can use the @EnablePrivateAccess annotation on a test class to call private (including static) methods, read and modify private or final fields without IDE warnings.
Call private methods (including static)
Read private fields (including static)
Modify private fields (including static)
Modify constant (final) fields
Example usage is shown in the DemoPrivateAccessTest class of the java-demo project.
Method 2: PrivateAccessor utility class
When the annotation is undesirable, the PrivateAccessor class provides static methods to manipulate private members: PrivateAccessor.get(targetObject, "fieldName") – read a private field PrivateAccessor.set(targetObject, "fieldName", newValue) – modify a private or final field PrivateAccessor.invoke(targetObject, "methodName", args...) – invoke a private method PrivateAccessor.getStatic(Class, "staticField") – read a private static field PrivateAccessor.setStatic(Class, "staticField", newValue) – modify a private static field or constant PrivateAccessor.invokeStatic(Class, "staticMethod", args...) – invoke a private static method
Fast mocking of arbitrary method calls
Unlike traditional class‑level mocking tools, TestableMock allows users to define a mock for a single method using the @MockMethod annotation. The mock method must have the same signature as the target method, with an additional first parameter representing the original object (or null for static methods).
Two rules summarize the approach: Mock non‑constructor methods by copying the original signature, adding a first parameter of the caller type, and annotating with @MockMethod . Mock constructors by copying the signature, changing the return type to the constructed type, giving any method name, and annotating with @MockContructor .
Example mock for String.substring(int, int):
@MockMethod
private String substring(String self, int i, int j) {
return "sub_string";
}Using targetMethod to specify the mocked method name:
@MockMethod(targetMethod = "substring")
private String anyName(String self, int i, int j) {
return "sub_string";
}Testing void methods
Void methods produce side effects such as modifying fields, calling external methods, or printing logs. TestableMock’s private‑field access and mock verifier enable checking these effects.
Modifying external variables
Example class with a private cache map and a void method that updates it:
class Demo {
private Map<String, Integer> hashCache = mapOf();
public void updateCache(String domain, String key) {
String cacheKey = domain + "::" + key;
Integer num = hashCache.get(cacheKey);
hashCache.put(cacheKey, num == null ? initHash(key) : nextHash(num, key));
}
}Test reads the private field after invoking the method:
@EnablePrivateAccess
class DemoTest {
private Demo demo = new Demo();
@Test
public void testUpdateCache() {
Integer first = demo.initHash("hello");
Integer next = demo.nextHash(first, "hello");
demo.updateCache("demo", "hello");
assertEquals(first, demo.hashCache.get("demo::hello"));
demo.updateCache("demo", "hello");
assertEquals(next, demo.hashCache.get("demo::hello"));
}
}Mocking external method calls
To intercept System.out.println, define a mock method:
@MockMethod
public void println(PrintStream ps, String msg) {
ps.println(msg); // optionally forward the call
}Verification example:
@Test
public void testRecordAction() {
Action action = new Action("click", ":download");
demo.recordAction(action);
verify("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \[click\] :download"));
}Project address
Open‑source repository: https://gitee.com/mirrors/TestableMock
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
