|
| 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