How to Build a Powerful Mock Plugin for Dubbo Services in CI Tests
This article explains how to design and implement a mock plugin for Dubbo-based services, enabling stable CI integration, multi‑service mocking, per‑test case responses, custom annotations, and service‑chain routing, with detailed code examples and deployment steps.
Introduction
During interface testing, developers often face unstable dependent services, high cost of constructing abnormal data, and missing dependent services before they are ready. These issues require a solution that can isolate the system under test from external resources.
What is Mock?
Mock is a technique that replaces hard‑to‑obtain or unstable objects with virtual ones, allowing tests to run without relying on external resources.
Mock Classification
Mock data – used mainly for unit tests (e.g., EasyMock, Mockito).
Mock service – a virtual service that returns predefined responses, suitable for integration tests.
Requirement Analysis
Return specified values when calling dependent interfaces.
Support mocking multiple services simultaneously.
Allow different test cases that depend on the same interface to receive different results.
Integrate smoothly into daily CI pipelines.
Enable selective mocking within a test suite.
Support Youzan service‑chain routing mode.
Design Overview
The plugin consists of two parts: bit-mocker (a jar that provides TestNG listeners) and trade-mocker-service (the mock service implementation).
Key components:
IMethodInterceptor – groups mock test cases and runs them first.
TestListenerAdapter – registers mock services before the suite starts, injects per‑test mock data on onTestStart, and deregisters services on onFinish.
MockFilter – intercepts Dubbo invocations, rewrites method name and parameters, and forwards the call to MockGenericService.
MockGenericService – implements Dubbo’s GenericService, stores a map of method‑to‑response, converts the stored value to the required return type using Gson.
Implementation Details
Creating a Provider
1. Pull the target jar from Maven.
2. Configure the provider programmatically (no XML) using ServiceConfig, ApplicationConfig, ProtocolConfig, and register it to the registry (ETCD, ZooKeeper, etc.).
// simplified provider registration code
ServiceConfig serviceConfig = new ServiceConfig();
serviceConfig.setApplication(new ApplicationConfig(appName));
serviceConfig.setProtocol(new ProtocolConfig(PROTOCOL, port));
serviceConfig.setInterface(service);
serviceConfig.setRef(new MockGenericService(map));
serviceConfig.export();Mock Filter
The filter extracts the interface, method name and arguments from the incoming Invocation, builds a new RpcInvocation that points to the generic $invoke method, and forwards it to the mock service.
public Result invoke(Invoker<?> invoker, Invocation invocation) {
RpcInvocation rpcInvocation = (RpcInvocation) invocation;
rpcInvocation.setMethodName(Constants.$INVOKE);
// set generic parameter types and arguments …
return invoker.invoke(rpcInvocation);
}Generic Service Logic
When $invoke is called, MockGenericService looks up the predefined response, converts it to the expected return type, and returns it. Errors such as missing methods or class‑not‑found are wrapped in MockException.
public Object $invoke(String method, String[] parameterTypes, Object[] args) {
Object value = map.get(method);
// convert value to return type …
return new Gson().fromJson(value.toString(), methodReturnType);
}TestNG Listener and Annotations
Two custom annotations are introduced: @Mock – placed on the test class, specifies the application, service, groupId and artifactId to be mocked. @MockData – placed on a test method, defines the target mock method and the local method that provides the mock response.
The listener registers all services in onStart, injects mock data in onTestStart, and deregisters services in onFinish. A JVM shutdown hook also ensures cleanup.
Selective Mocking and Test Ordering
Mock test cases are grouped by the TestNG mock group and placed at the beginning of the execution list using IMethodInterceptor. After the mock cases finish, the services are taken down so that ordinary test cases see the real implementation.
Service‑Chain Routing Support
If a service‑chain identifier (sc) is present, the plugin registers the mock service with the same sc; otherwise it generates a temporary sc to avoid affecting the base environment. The plugin prevents registering a mock service when the same sc already has a real instance.
Usage Guide
Add the bit-mocker jar to the test project.
Annotate the test class with @Listeners({MockableListener.class}) and @Mock(...) to declare the services to mock.
Annotate each test method that needs a specific response with @MockData(mockMethod = "...", dataMethod = "...").
Write the mock data method to return a PlainResult (or other appropriate type) containing the expected response.
Run the test suite as usual; the listener handles registration, injection and cleanup automatically.
Best Practices
Mock only unstable or costly dependencies, not core business logic.
Ensure the mocked service does not interfere with other test cases that require the real implementation.
Future Work
The plugin can be extended to provide finer‑grained control over mock lifecycles, support additional mock types, and integrate with other service discovery mechanisms.
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.
Youzan Coder
Official Youzan tech channel, delivering technical insights and occasional daily updates from the Youzan tech team.
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.
