TestableMock Documentation

repository·master·Indexed 23 days ago

https://github.com/alibaba/testable-mock

A lightweight mocking framework that simplifies unit testing by allowing developers to mock almost any method—including private, static, and constructors—using the @MockInvoke annotation. It utilizes a Java agent for runtime bytecode modification to redirect method invocations from a business class to a dedicated mock container class, reducing the boilerplate and complex setup associated with traditional mocking tools.

Tokens
12.5K
Snippets
28
Records
71
Agent score
84%

What's inside TestableMock

  1. Introduction to TestableMock

    master

    TestableMock is a lightweight and 'maverick' mocking tool designed to simplify unit testing by reducing the overhead associated with traditional mocking frameworks.

    Traditional frameworks often require developers to manage complex setup tasks such as framework initialization, compatibility with testing frameworks, handling private or static methods, managing object instantiation (e.g., new vs. injection), and manually injecting mock objects into the class under test.

    TestableMock aims to solve this by allowing developers to focus solely on the core requirement: identifying which method calls should be replaced with fake mock methods during testing, without the distraction of non-critical configuration and boilerplate.

  2. Overview of TestableMock

    master
    TestableMock is a tool designed to accelerate mock writing and simplify unit testing. It is designed to work with any test framework and requires no manual initialization. It can mock private methods, static methods, constructors, and other method types regardless of how the object was created. The primary workflow involves writing a mock method and adding the @MockInvoke annotation to it.
  3. The relationship between Business, Test, and Mock classes

    master

    TestableMock operates on a tripartite relationship to manage mock lifecycles and scope:

    1. Class Under Test (Business Class): The actual production code being tested. The Java agent modifies this class to redirect specific method calls to the mock container.
    2. Test Class: The class containing your unit tests. TestableMock automatically inserts mock context initialization code at the beginning of every test case within this class.
    3. Mock Container Class: The class containing the replacement logic. TestableMock adds a testableIns() method to turn this into a singleton and inserts recording logic at the start of each mock method to track calls.
  4. Compatibility with other mock tools and frameworks

    master

    Mocking Tools

    • Compatible: TestableMock works safely with tools based on dynamic proxies, such as Mockito, EasyMock, and Spock.
    • Use with caution: Tools that modify class loaders or bytecode (e.g., PowerMock, JMockit) may have compatibility risks.

    Test Frameworks

    • Core functionality: PrivateAccessor, OmniConstructor, and OmniAccessor are framework-independent and work with any test framework.
    • Mock invocation verifier: Currently provides appropriate support for JUnit 4, JUnit 5, TestNG, and Spock.
  5. How TestableMock works

    master

    TestableMock uses a Java agent to dynamically modify bytecode at runtime. Instead of defining mocks inside every individual test case, TestableMock follows a 'convention over configuration' approach where each business class is paired with its own independent 'Mock class'.

    The Core Mechanism: Before a unit test runs, the Java agent intercepts the loading of classes and replaces all method invocations within the 'class under test' that match a defined mock method with an invocation to the corresponding method in the 'mock container class'.

    Key Characteristics:

    • Targeted Replacement: It mocks the invocation of a method within the class under test, not the method definition itself. The code inside your actual test case remains unmocked.
    • Framework Agnostic: It works regardless of whether the target object is injected by a framework (like Spring), created via new, or if the method is private, static, inherited, or overloaded.
    • Scope Control: The system establishes associations between the 'class under test', the 'test class', and the 'mock container class' to ensure mocks are only active within the intended scope.
  6. Understand the TestableMock module structure

    master

    The project is organized into several specialized modules. Depending on your integration needs, you may interact with different parts of the repository:

    • testable-all: An aggregate dependency module that allows you to include all sub-module functionalities at once.
    • testable-core: The base module providing Mock-related annotations (like @MockInvoke) and utility classes.
    • testable-agent: A JavaAgent module that provides the core Mock testing functionality.
    • testable-processor: A compile-time code preprocessing module for test assistance.
    • testable-maven-plugin: A Maven plugin used to simplify the injection of the testable-agent.
    • demo/: Contains example implementations for Java, Kotlin, Android, and the Spock testing framework.
  7. Configure custom test class and mock container locations

    master

    By default, TestableMock expects:

    1. The test class to be named <ClassUnderTest>Test in the same package as the class under test.
    2. The mock container to be a static inner class named Mock inside the test class, OR an independent class named <ClassUnderTest>Mock in the same package.

    If your project does not follow these naming conventions, use the @MockWith annotation to explicitly specify the test class and its associated mock container.

  8. Create Mock Methods using the IntelliJ Plugin

    master

    The plugin provides two ways to convert method invocations into mock method definitions:

    • Copy Testable Mock-Method: Select a method invocation, right-click, and choose this option. It converts the method signature into a mock method definition and copies it to your clipboard.
    • Copy Mock-Method To TestClass: Select a method invocation, right-click, and choose this option. It directly inserts the converted mock method into the corresponding mock class.
  9. Generate documentation locally

    master

    The project documentation is generated using docsify. To view the documentation locally, follow these steps:

    1. Install Node.js.
    2. Install the docsify CLI globally using npm:
      npm install -g docsify
    3. Serve the documentation from the docs directory:
      docsify serve docs
    docsify serve docs
  10. Upgrade to TestableMock v0.7

    master

    In version 0.7, several mock-related annotations and methods were renamed to avoid naming conflicts (specifically with com.sun package static methods) and to better reflect their usage. The functional behavior remains unchanged; only the names need to be updated in your code.

    Renamed Methods:

    • verify() $\rightarrow$ verifyInvoked()

    Renamed Annotations:

    • @MockMethod $\rightarrow$ @MockInvoke
    • @MockConstructor $\rightarrow$ @MockNew
  11. Verify Mock Invocation in TestableMock

    master

    When testing methods that contain external dependencies, you can use verifyInvoked along with verifiers and matchers to ensure that mock methods are called with the expected parameters.

    Basic usage pattern:

    1. Execute the method under test.
    2. Call verifyInvoked("methodName").
    3. Chain a verifier (like .with(...)) to specify parameter expectations.
    @Test
    public test_case() {
        int res = insToTest.methodToTest();
        verifyInvoked("mockMethod").with(123, "abc");
    }