TL;DR: API mocking simplifies testing by simulating API responses, improving performance, reliability, and control. This blog contains strategies and best practices for unit testing.
Software testing and verification are essential for modern development, but the complexity of customer requirements and third-party integrations can make them daunting. Although these third-party integrations improve app functionality, testing them can be a nightmare for developers. There can be issues with security, performance, data incompatibilities, error handling, dependencies, and more.
As a result, developers are now using various mocking techniques to address these issues. This article will explain why mocking is so popular, and how API requests can be mocked to improve unit test coverage.
Why mocking is the answer?
Mocking API requests becomes an invaluable strategy for overcoming these challenges. It allows you to simulate API responses and test features without sending network requests to external APIs. By eliminating actual request processing, mocking boosts performance, reliability, security, and control over data during testing.
Accelerate the tests and ensure their consistency
As part of their jobs, it falls on the shoulders of developers to perform unit tests while developing software. These tests must be executed to give all results in the shortest time possible. API mocking ensures the testing cycle is free of excess delays related to external servers and latency due to networks. Tests have quick and consistent run times.
Ownership of data and state
When testing using real API calls, exchanging large amounts of data can make the tests unpredictable. It is advisable to use mocking because it permits developers to state what they want the responses of tests to be, thus avoiding surprises in responses that may not be consistent with normal tests.
Concentrate on individual units
Unit tests focus on verifying the behaviors of small parts of your app. Mocking can isolate these components and test them separately without depending on any other components, making your tests more focused and meaningful.
Strategies for mocking API requests
There are several strategies for creating a Mock API. There is no best strategy, so you can choose the strategy most suitable to your needs. The complexity you want to extend with your API will be a factor in choosing the right strategy. You can look at the following strategies and identify their apps.
Service virtualization (Mocking APIs using tools)
- Postman: It is one of the most popular tools used for API mocking. It allows you to specify response headers, delay times, status codes, and response content in various formats such as JSON, XML, and plain text.
- Mockoon: This tool has a simple interface that lets you quickly build your mocks using ready-made templates. You can run your mock app in a serverless, headless, or automated environment. Also, Mockoon lets you serve your mock API over TLS with a custom certificate.
- WireMock: This tool is free to use. It lets you record real API interactions and then automatically create mock endpoints based on those interactions.
Manual mocks
Manual mocks involve creating mock functions or modules that replace the real HTTP request mechanisms. This can be as simple as overriding a function that makes an HTTP request with one that returns a predefined (hard-coded) response.
// Example of a manual mock. const fetchUserFromMockApi = (userId) => { return Promise.resolve({ id: userId, name: "Mock User" }); }; // Inject the mock function. const fetchUser = createFetchUser(fetchUserFromMockApi); // Test function. const runTests = () => { fetchUser(1) .then((user) => { if (user.id === 1 && user.name === "Mock User") { console.log("Test passed"); } else { console.log("Test failed"); } }) .catch((error) => { console.error("Test failed with error:", error); }); };
The results of the previous implementation are shown in the following screenshot.
You can find the full implementation with this link.
While manual mocks offer great flexibility and control, they can become hard to manage with complex APIs or when used extensively across test suites. Also, they are time-consuming as the complexity increases.
Using mocking libraries
Several libraries and tools have been developed to streamline the process of mocking API requests. Libraries such as Nock for Node.js, Mockito for Java, and Responses for Python have rich APIs for mocking.
Nock example
const nock = require('nock'); const chai = require('chai'); const fetchUser = require('../src/userService'); const { expect } = chai; describe('fetchUser', () => { before(() => { // Mocking an HTTP GET request to a user service. nock('https://api.example.com') .get('/users/1') .reply(200, { id: 1, name: 'Mock User' }); }); it('should return a mocked user', async () => { const user = await fetchUser(1); expect(user).to.deep.equal({ id: 1, name: 'Mock User' }); }); after(() => { nock.cleanAll(); }); });
The code in the previous implementation produces the results shown in the following screenshot.
The complete code example is available here.
Steps to Mocking API requests
Regardless of the method you use to mock API requests, there must be a process to follow. The reason is that the validity of unit tests depends on what we build here. So, we decided to include a checklist to help you create a Mock API with minimal deviation from the real API:
1. Identify the API requests to mock
Start by identifying the external services and endpoints with which your app interacts. Think about which interactions affect the outcomes of your unit tests and should be mocked.
2. Choose your mocking strategy
Based on your API interaction’s complexity and project needs, choose a mocking strategy. Manual mocks may be sufficient for simple scenarios, while more complex integrations could benefit from a mocking library or service virtualization.
3. Implement mocks
The way you execute your plan depends on the strategy choice. Ensure that your trial runs accurately and fits the responses given to your app with the expected response from the call to the live API. This includes both successful responses and error situations.
4. Integrate mocks into unit tests
After preparing your mock objects, you can include them in your test suite with your mock functions or configure a mocking library within your test setup. If you are using service virtualization, start the virtual service or tool before running your test suite.
5. Refine mock APIs based on test results
After running your tests with the mocked APIs, carefully observe any failures or unexpected behavior, as these can expose differences between your mock implementations and the real API behavior. You can adjust your mocks to ensure they cover the testing scenarios accurately.
Best practices for mocking API requests
When performing unit testing using the mock API, follow some good practices. These practices will help developers minimize API overfitting and nonfunctional issues.
Ensure mocks reflect real API behavior
Your mocks are only as good as their fidelity to the behavior of the real API. A mock must support the same transport protocols, schemes, etc., as the real API. The mock API should be accessible exactly like the original service and require no special tools or libraries.
Use realistic data
While mocking allows you to use static responses, using data that’s too simplistic can lead to tests that pass despite real-world scenarios that would cause failures. Ensure your mocked responses include realistic data.
Negative test cases
Simulate unexpected errors, long response times, or even invalid messages, and make sure your API client handles them gracefully. This is a way of testing the potential non-functionalities of the client or your software.
Easily build real-time apps with Syncfusion’s high-performance, lightweight, modular, and responsive JavaScript UI components.
Conclusion
Mocking API calls is an effective way to achieve comprehensive unit test coverage for apps that depend on external services. It isolates your unit tests from external interactions, making them quick, consistent, and clean.
Whether you opt for manual mocks, mock libraries, or service virtualization, the key to successful mocking is accurately emulating the real-world APIs your app relies on. However, it’s good to stay informed and open to changes to refine your mocking strategies and get the best result for your project.