How Xianyu Simplified Order‑Detail Mock Testing with a Three‑Layer Plugin Architecture
This article explains how Xianyu tackled the complexity of order‑detail page status handling by designing a three‑layer mock solution that decouples mock logic, provides business‑semantic state switching, and enables fast local and device debugging, dramatically reducing integration and testing costs.
Background
Xianyu, China’s largest second‑hand e‑commerce platform, offers specialized transaction flows such as "Verification Treasure". An order‑detail page must reflect dozens of fine‑grained status variations, each with different text, buttons, and progress bars.
Problems
The page’s status view depends on API fields, creating heavy communication overhead with backend services. Existing mock approaches require manual status value changes, rely on Charles proxy for device debugging, and impose high testing and visual‑regression costs for developers, testers, and UI designers.
Long development integration cycles; self‑testing is insufficient. High testing and visual regression effort. Slow online issue diagnosis and resolution.
Requirement Analysis
To make mocking easier, the solution must satisfy four criteria:
Debug convenience : work both on local PC and real devices.
Business semantics : state switching should convey meaningful business intent, not just a raw field change.
Code decoupling : mock logic must stay out of production code.
Mock data simplification : a single mock file should be extensible for all status variations, with only the differing fields overridden.
Technical Solution Overview
The design consists of three modules: a glue layer (compile‑time plugin), a mock layer (runtime request interceptor), and a view layer (UI component for state selection).
Glue Layer Implementation
The glue layer is a compilation‑time base that injects the view and mock plugins into the business bundle only for non‑production builds. It uses the rax‑app plugin system, which extends the build‑scripts toolchain.
{
"plugins": [
[
"build-plugin-rax-app",
{
"targets": ["web"],
"type": "mpa"
}
],
"@ali/build-plugin-rax-mock",
"./selfBuild"
]
}During the webpack configuration phase, the plugin adds a debug entry point to order pages only:
if (api.context.command === 'build') return;
api.onGetWebpackConfig('web', config => {
config.entryPoints.values().forEach(entry => {
const entrys = entry.values();
const entryName = entrys[1];
if (!/pages\/Order\/index$/.test(entryName)) return;
const debugOrderPath = path.resolve(__dirname, 'src/components/DebugOrder');
const newOrderPage = `${__filename}?debugOrderPath=${debugOrderPath}!${entryName}`;
entry.clear();
entry.add(entrys[0]);
entry.add(newOrderPage);
});
});Mock Layer Implementation
The mock layer generates a mock.json collection that covers all required fields for every order status. A typical response shape is:
{
"api": "mtop.a.order.info",
"data": {
"status": 0,
"orderStatus": 1001,
...,
"trade": {
"actions": [],
"amount": "2189.00",
"attributes": {
"consis": "10",
"ultronPP": "a_3_0@c"
}
}
},
"ret": ["SUCCESS::调用成功"],
"v": "1.0"
}At runtime, a Proxy intercepts the global window.lib.mtop object and rewrites its request method. When mock mode is active, the request is redirected to a local server (e.g., http://127.0.0.1/_mtop_mock_/com.test.one); otherwise the original request proceeds.
lib = window.lib;
const getMtop = originValue => new Proxy(originValue, {
set(target, p, v, r) {
if (p === 'request' || p === 'H5Request') {
Reflect.set(target, p, getRequest(v), r);
} else {
Reflect.set(target, p, v, r);
}
return true;
}
});
if (!lib) {
lib = new Proxy({}, { /* ... */ });
} else if (!lib.mtop) {
lib.mtop = getMtop({});
} else {
lib.mtop.request = getRequest(lib.mtop.request);
lib.mtop.H5Request = getRequest(lib.mtop.H5Request);
}
function getRequest(originRequest) {
return async function () {
if (getMockSwitch()) {
// redirect to local mock
}
return originRequest();
};
}View Layer Implementation
The view layer adds a draggable icon on the page. Clicking it opens a modal that displays a tree of enumerated order states. The tree is built from a TypeScript interface:
export interface ClassifyDataItem {
[key: string]: any;
/** Node name */
nodeName: string;
/** Enum value */
node?: StatusEnum;
/** Main status code */
status?: string;
/** Sub‑status code */
subStatus?: string;
childNode?: ClassifyDataItem[];
}
export const classifyData: ClassifyDataItem[] = [
{
nodeName: 'Order placed, waiting for courier',
status: '1',
subStatus: '10',
node: StatusEnum.BUYER_CREATE,
childNode: [/* ... */]
}
// ... more nodes
];When a node is clicked, the component writes the corresponding status and subStatus into the mock JSON, triggers window.location.reload(), and the page fetches the new mocked data, instantly reflecting the selected state.
Business Application
After wiring the three modules, developers only need to define their own order‑status enumeration and import the view and mock plugins. The solution has been deployed in Xianyu’s Verification Treasure and luxury‑consignment flows, cutting the time to switch a status from minutes to seconds and reducing integration communication cost by over 30 %.
It also helps new team members quickly understand business logic because the mock UI presents states with clear semantic labels.
Summary and Outlook
The three‑layer mock system achieves the four initial goals: easy debugging, business‑semantic state switching, code decoupling, and streamlined mock data. Future work includes extending the approach to multi‑page transaction flows and enabling linked status updates across the entire order lifecycle.
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.
