Skip to content

Commit 18b61bd

Browse files
authored
Merge branch 'main' into add-changelogs-guidelines
2 parents 83dada4 + 9dba069 commit 18b61bd

1 file changed

Lines changed: 176 additions & 0 deletions

File tree

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
2+
# Mocking APIs in MetaMask Mobile for Enhanced E2E Testing
3+
4+
## Introduction
5+
6+
This document outlines how MetaMask Mobile uses API mocking to boost End-to-End (E2E) testing. Mocking lets us simulate different conditions that the app might face, ensuring it functions reliably across various scenarios.
7+
8+
## Mocking vs. E2E Testing
9+
10+
While E2E tests verify the overall user experience, mocking focuses on testing individual components. Here’s how they differ:
11+
12+
- **E2E Tests**: These validate the app's functionality as a whole, interacting with real APIs and backend services.
13+
- **Mocking**: Simulates responses from APIs, allowing us to test components in specific network conditions or edge cases.
14+
15+
We use mocking to enhance our E2E testing, especially for scenarios where E2E alone might be unreliable or tricky to set up.
16+
17+
## File Structure
18+
19+
We keep E2E and mock tests separate with file naming conventions:
20+
```
21+
root/
22+
├── e2e/
23+
│ ├── spec/
24+
│ │ ├── Accounts/
25+
│ │ │ └── spec.js
26+
mock.spec.js# Mock test for Accounts
27+
│ │ ├── Transactions/
28+
│ │ │ └── spec.js
29+
mock.spec.js # Mock test for Transactions
30+
│ ├── api-mocking/
31+
│ ├── api-mocking/
32+
│ │ ├── mock-responses/
33+
│ │ │ ├── gas-api-responses.json
34+
```
35+
This structure promotes clear organisation and makes managing tests simpler.
36+
37+
## Mock Server Implementation
38+
39+
### Overview
40+
41+
We use Mockttp to simulate API responses, providing flexible testing across different HTTP methods. This allows us to test app behaviour even when external dependencies are unavailable or unreliable.
42+
43+
### Key Features
44+
45+
- Supports multiple HTTP methods (GET, POST, etc.)
46+
- Configurable requests and responses
47+
- Logs responses to simplify debugging
48+
49+
### Naming Mock Test Files
50+
51+
Mock test files are named with `.mock.spec.js` to keep things organised. For example, a test for the suggested gas API would be named: `suggestedGasApi.mock.spec.js`.
52+
53+
### Setting Up the Mock Server
54+
55+
The `startMockServer` function in `e2e/api-mocking/mock-server.js` starts the mock server. It takes events organised by HTTP methods, specifying the endpoint, response data, and request body (for POST requests).
56+
57+
```javascript
58+
import { mockEvents } from '../api-mocking/mock-config/mock-events';
59+
60+
mockServer = await startMockServer({
61+
GET: [
62+
mockEvents.GET.suggestedGasApiErrorResponse,
63+
],
64+
POST: [
65+
mockEvents.POST.suggestedGasApiPostResponse,
66+
],
67+
});
68+
```
69+
70+
### Defining Mock Events
71+
72+
`mockEvents.js` defines mock events, including:
73+
74+
- `urlEndpoint`: The API endpoint being mocked
75+
- `response`: The mock response the server will return
76+
- `requestBody`: Expected request body (for POST requests)
77+
78+
```javascript
79+
export const mockEvents = {
80+
GET: {
81+
suggestedGasApiErrorResponse: {
82+
urlEndpoint: 'https://gas.api.cx.metamask.io/networks/1/suggestedGasFees',
83+
response: { status: 500, message: 'Internal Server Error' },
84+
},
85+
},
86+
POST: {
87+
suggestedGasApiPostResponse: {
88+
urlEndpoint: 'https://gas.api.cx.metamask.io/networks/1/suggestedGasFees',
89+
response: { status: 200, message: 'Success' },
90+
requestBody: { priorityFee: '2', maxFee: '2.000855333' },
91+
},
92+
},
93+
};
94+
```
95+
96+
### Response Structure
97+
98+
Mock responses are stored in individual JSON files for each API or service within the `mock-responses` folder, making them easier to maintain and manage. Each API service has its own JSON response file, such as `gasApiResponse.json` for gas-related responses and `ethpriceResponse.json` for Ethereum price responses. This organisation enables clear separation of mock data and simplifies updates or additions.
99+
100+
**Example:** `gasApiResponse.json`
101+
```json
102+
{
103+
"suggestedGasApiResponses": {
104+
"error": {
105+
"message": "Internal Server Error"
106+
}
107+
},
108+
"suggestedGasFeesApiGanache": {
109+
// ... detailed gas fee data ...
110+
}
111+
}
112+
```
113+
114+
## Logging
115+
116+
The mock server logs response statuses and bodies to help track mocked requests, making debugging more straightforward.
117+
118+
## Using Mock Testing Effectively
119+
120+
121+
### When to Use Mocks:
122+
123+
- For testing isolated features without relying on live data
124+
- For testing edge cases that are tricky to reproduce with real data
125+
- For deterministic test results by controlling inputs and outputs
126+
127+
### When Not to Use Mocks:
128+
129+
- Stable Live Environments: When APIs and services are reliable, testing live ensures production-like accuracy.
130+
- Integration Testing: Live tests validate interactions with third-party services, capturing real-world behaviour.
131+
- Performance Testing: Only live environments provide accurate latency and throughput metrics.
132+
- Dynamic Data Scenarios: Features relying on user data or complex workflows may reveal issues that mocks miss.
133+
134+
There should be a mix of tests that verify real-life services and some that use mocks, when applicable, to achieve comprehensive coverage.
135+
136+
137+
### Utilizing Fixtures with testSpecificMock
138+
139+
For more complex mock events or criteria, you can use the `mockSpecificTest` object to define custom mock events.
140+
141+
When using fixtures for E2E tests, you can leverage the `testSpecificMock` object to inject specific mocks into your test cases. This allows you to dynamically modify mock responses based on the scenario under test. For example:
142+
143+
```javascript
144+
test.use({
145+
testSpecificMock: {
146+
GET: [
147+
{
148+
urlEndpoint: 'https://gas.api.cx.metamask.io/networks/1/suggestedGasFees',
149+
response: { status: 200, message: 'Custom Success Response' },
150+
},
151+
],
152+
},
153+
});
154+
```
155+
156+
This approach is particularly useful for targeted scenarios where pre-defined mock events in `mockEvents.js` might not be sufficient or applicable.
157+
158+
For example, see this [GitHub Reference](https://github.com/MetaMask/metamask-mobile/blob/d1946d2037399356b7b582b07e09b0528a68e0ac/e2e/specs/confirmations/advanced-gas-fees.mock.spec.js#L30).
159+
160+
### When mockEvents May Not Be Suitable
161+
162+
In certain cases, using `mockEvents` might not be ideal—for instance, when:
163+
164+
- The mock responses need to be dynamically created based on the test input.
165+
- The scenario being tested is unique and requires a one-off response that doesn’t fit the general structure of `mockEvents`.
166+
- Reusing existing mocks may lead to inconsistencies in the expected results.
167+
168+
In such cases, `testSpecificMock` is a more suitable option, allowing you to define bespoke mocks for individual tests.
169+
170+
### Current Workaround for Network Request Interception
171+
172+
Due to limitations in intercepting network requests directly, we currently route traffic through a proxy server. This lets us intercept and mock requests as needed.
173+
174+
## Future Improvements
175+
176+
We’re looking into further enhancements for our mocking setup to simplify processes and improve test coverage.

0 commit comments

Comments
 (0)