Fluent Mock is a mock tool that implements mock Java classes, interfaces, and objects in the test process using the two black technologies provided by Java: Instrument (Java Agent) and Annotation Processor.

How does Fluent Mock compare to mainstream mock frameworks, and why does it exist?

Problem # 1: Compare to other mock frameworks on the market

There are a variety of mock frameworks on the market, including Easy-Mock, JMock, Mockito, power-Mock, and Jmockit. Why fluent?

Let’s start with an overview of the moves and pros and cons of various mock frameworks

The framework The principle of The mock object The mock range Jacoco
Mockito A dynamic proxy Object to create a mock object and then specify the behavior for it You cannot Mock private/static and constructors Compatible with
PowerMock Class loader Object to create a mock object and then specify the behavior for it Any way Are not compatible
EasyMock A dynamic proxy Object to create a mock object and then specify the behavior for it You cannot Mock private/static and constructors Compatible with
jMock A dynamic proxy Object to create a mock object and then specify the behavior for it You cannot Mock private/static and constructors Compatible with
jMockit instrument Class, interface. Modifying class bytecodes without first creating mock objects is valid for all instances of the class Any method (high versions limit mocks of private methods) Compatible with
Fluent-Mock instrument Classes, interfaces, objects. Modifying class bytecodes can take effect for all instances of the class, or for specific instances, without creating mock objects first. Any method other than the native qualification Compatible with

From the technical system, can be basically divided into two categories

  • The use of dynamic proxies (or classloaders) is mainly traditional mock frameworks: Mockito, PowerMock, easyMock, jMock, etc. Due to the limitations of technical characteristics, you can’t do anything you want with mock behavior
    1. Do not mock private functions, final classes, or final functions.
    2. You cannot mock constructors, static code blocks
    3. You need to use mock objects to replace existing objects, and you need to manage object dependencies. There is no way to specify behavior for an object instance from the original class new.
  • Jmockit and Fluent-Mock modify the bytecode logic of the original class, overcoming the limitation of dynamic proxy. Any object entering the method to be mocked executes the modified bytecode logic, without the need to maintain (replace) the original dependency.

Problem two: Compare with the JMockit framework

Since Fluent-Mock and JMockit use the same technology architecture, and JMockit has evolved over the years, what’s so special about Fluent-Mock? The author has also been a heavy user of JMockit for years because of its simplicity and convenience compared to other mock frameworks, especially new MockUP and @Mock. After years of use, Jmockit is still unsatisfactory, mainly manifested in:

  1. The syntax of Expectations is quite weak, may not be the same view, personal early use, behind basically give up this usage.
  2. You can mock all methods with a new MockUp, but if the original method name and parameters are refactored, it will not be detected by compilation.
  3. While earlier versions mock private methods, later versions mock out private methods.
  4. The New MockUp method is valid for all instances of the specified class, but does not affect other instances.
  5. The syntax for specifying mock behavior is a bit less smooth than other mock frameworks.

Fluent-mock is designed to address the above shortcomings by incorporating the new MockUp + @Mock feature of the JMockit framework and dynamically compiling and generating mock behavior guidance methods based on the class meta-information to be mocked. Taking advantage of the Fluent interface, you can specify mock behaviors smoothly without memory. You can mock not only generically, but also specifically (on a given object).

Here is a code to briefly feel the features of Fluent-Mock:

/** * Mock MyServiceImpl */
@Mocks({MyServiceImpl.class})
public class MyServiceImplTest {
    /** This class is generated by AnnotationProcessor, which provides fluent syntax guidance **/
    private MyServiceImplTestMocks mocks = new MyServiceImplTestMocks();

    @displayName (" Verify Fluent Mock to mock the specified instance ")
    @Test
    public void test(a) {
        /** create 3 instances **/
        MyServiceImpl myService1 = new MyServiceImpl();
        MyServiceImpl myService2 = new MyServiceImpl();
        MyServiceImpl myService3 = new MyServiceImpl();
        
        /** specify behavior only for instance 1 and instance 3, leaving instance 2 **/
        mocks.MyServiceImpl(myService1).sayHello2.thenReturn("1");
        mocks.MyServiceImpl(myService3).sayHello2.thenReturn("3");
            
        String result1 = myService1.sayHello("m");
        String result2 = myService2.sayHello("m");
        String result3 = myService3.sayHello("m");
        /** Verify interface behavior **/
        Assertions.assertEquals("1", result1);
        Assertions.assertEquals("hello:m", result2);
        Assertions.assertEquals("3", result3); }}public class MyServiceImpl {
    public String sayHello(String name) {
        return "hello:"+ name; }}Copy the code

Fluent in mock configuration

Maven configuration

  • The jar package reference

In the project pom.xml file

<project>
    <properties>
        <fluent-mock.version>1.0.4</fluent-mock.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.test4j</groupId>
            <artifactId>fluent-mock</artifactId>
            <version>${fluent-mock.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
Copy the code
  • And surefire

To be used with the Maven-Surefire-plugin, you need to configure parameters in the argLine parameter

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <argLine>-javaagent:${settings.localRepository}/org/test4j/fluent-mock/${fluent-mock.version}/fluent-mock-${fluent-mock.version}. jar</argLine>
            </configuration>
        </plugin>
    </plugins>
</build>
Copy the code
  • With Surefire + Jacoco

If jacoco is also used to count unit test coverage, add an @{argLine} to the configuration

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <argLine>@{argLine} -javaagent:${settings.localRepository}/org/test4j/fluent-mock/${fluent-mock.version}/fluent-mock-${fluent-mock.version}. jar</argLine>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.6</version>
            <executions>
                <execution>
                    <id>default-prepare-agent</id>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                </execution>
                <execution>
                    <id>default-report</id>
                    <goals>
                        <goal>report</goal>
                    </goals>
                    <phase>test</phase>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
Copy the code

Gradle configuration

dependencies {
    testCompile('org.test4j:fluent-mock:${fluent-mock.version}')
    //annotationProcessor('org.test4j:fluent-mock:${fluent-mock.version}')
    testAnnotationProcessor('org.test4j:fluent-mock:${fluent-mock.version}')

    test {
        jvmArgs "-javaagent:${classpath.find { it.name.contains("fluent-mock") }.absolutePath}"
        useJUnitPlatform()
    }
}

Copy the code

IDE configuration

In most cases, the IDE automatically recognizes maven and Gradle configurations. If the mock framework doesn’t work, you can add:

-javaAgent :{your local repository root path}/org/test4j/fluent-mock/1.0.4/fluent-mock-1.0.4.jarCopy the code

link

Fluent Mock open source address

Fluent Mybatis open source address