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.

Youzan Coder
Youzan Coder
Youzan Coder
How to Build a Powerful Mock Plugin for Dubbo Services in CI Tests

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaDubboMockingCITestNGservice-mocking
Youzan Coder
Written by

Youzan Coder

Official Youzan tech channel, delivering technical insights and occasional daily updates from the Youzan tech team.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.