Skip to content

Commit 60543ae

Browse files
committed
add upto r2.3 of module 5
1 parent 1452a82 commit 60543ae

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Mocking
2+
We've mentioned mocking in the last couple of lessons, but what exactly is mocking and how is it helpful in writing tests? The objectives of this lesson are:
3+
1. Understanding the use of mocking in tests
4+
2. Understanding different ways to implement mocking
5+
6+
## What is mocking?
7+
8+
Sometimes a function, class or component may be hard to test for because:
9+
- The class has dependencies which are hard to provide (e.g. the constructor expects multiple arguments)
10+
- Some functionality of a class may not be accessible from outside (e.g. by using access modifiers like private in TypeScript)
11+
- The function/class/component does far too much that we don't want in our tests
12+
13+
In simple terms, mocking refers to replacing an actual piece of code with a simple piece of code that provides the required inputs by default. Mocking helps us write better tests because:
14+
- They eliminate functionality which we don't really want in the scope of a test.
15+
- Makes tests faster and less flaky by avoiding dependencies (e.g. access to a real database, using a third-party library)
16+
- Makes things easier to test. With mocking, we can easily create the ideal test setup to use in our tests.
17+
- Reduce the setup of our tests. Many libraries expect some setup to be done in order to work. With mocking, we can ignore all this and focus on testing actual functionality instead.
18+
- We don't need to test third-party code as it is probably already tested. In unit tests, we want to focus on the smaller parts of our application that we developed ourselves. Mocking third-party dependencies help to keep tests more focused on our custom logic and less about internal implementation details.
19+
20+
## Monkey patching
21+
22+
Monkey patching is a technique to add, modify, or suppress the default behavior of a piece of code at runtime without changing its original source code. It is a quick and easy way to implement mocking for tests.
23+
24+
Let's look at an example of a controller that depends on a database model.
25+
```js
26+
async function getBook(req, res) {
27+
const book = await booksDB.readById(req.bookId)
28+
res.json({book})
29+
}
30+
```
31+
By looking at this code, we can guess that `booksDB` must be an object with one property `readById` whose value is a function:
32+
```js
33+
{
34+
...
35+
readbyId: function(id) {
36+
...
37+
},
38+
...
39+
}
40+
```
41+
The other model functions make up the rest of the key-value pairs in this object.
42+
43+
In order to mock the database function, we can simply replace the specific `booksDB` function we are testing for with a determinisitc function.
44+
45+
So, let's say we want to test for the case where booksDB returns `undefined` for some reason. In our test file, we can write:
46+
```js
47+
booksDB.readById = function(id) {
48+
return undefined
49+
}
50+
```
51+
52+
There we go, now when we execute the test on our controller it will get the value `undefined` when it calls the `booksDB.readById` function. In this way, we can make `readById` return a specific result to be used by our controller in each different test case. Such as returning a specific book or an error message or an incomplete book object.
53+
54+
By using this technique, we are only testing how our controller handles different test cases and not testing the actual database model code which is a third-party.
55+
56+
However, we did mention in the beginning that monkey patching works without changing the original source code, but we modified the code here. To make sure we don't completely modify the code, we can do this:
57+
```js
58+
// Keep track of original readById
59+
const originalReadById = booksDB.readById
60+
// Change temporarily to mock function
61+
booksDB.readById = function(id) {
62+
return undefined
63+
}
64+
// Run some test with mock readById
65+
...
66+
...
67+
...
68+
// Restore original readById
69+
booksDB.readById = originalReadById
70+
```
71+
72+
This ensures that the code is only temporarily modified to the mock version for the specific scope where it is required, and then it goes back to its original version to be used by other parts of the file.
73+
74+
## Mocking in Jest
75+
76+
Although monkey patching does the job, Jest provides more sophisticated methods for mocking. Going by the same example as above, we can mock the `readById` function using:
77+
```js
78+
// Keep track of original readById
79+
const originalReadById = booksDB.readById
80+
// Change temporarily to mock function
81+
booksDB.readById = jest.fn((id) => undefined)
82+
// Run some test with mock readById
83+
...
84+
...
85+
...
86+
// Restore original readById
87+
booksDB.readById = originalReadById
88+
```
89+
90+
`jest.fn()` creates a new mock function. But we can also use some Jest functions to track the original function and restore it.
91+
```js
92+
// Keep track of original readById
93+
jest.spyOn(booksDB, "readById")
94+
// Change temporarily to mock function
95+
booksDB.readById.mockImplementation((id) => undefined)
96+
// Run some test with mock readById
97+
...
98+
...
99+
...
100+
// Restore original readById
101+
booksDB.readById.mockRestore()
102+
```
103+
104+
The `spyOn`, `mockImplementation` and `mockRestore` functions help to track, implement mocking on and restore a function.
105+
106+
Another advantage of using Jest mock functions, is to run assertions on the function calls:
107+
```js
108+
// The mock function was called at least once
109+
expect(mockFunc).toHaveBeenCalled();
110+
111+
// The mock function was called at least once with the specified args
112+
expect(mockFunc).toHaveBeenCalledWith(arg1, arg2);
113+
114+
// The last call to the mock function was called with the specified args
115+
expect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2);
116+
```
117+
118+
We can eliminate the actual functionality of third-party functions and focus simply on how, when and with what parameters was the function called. It helps us test our controllers' or services' interaction with the third-party functions.
119+
120+
Using Jest, you can even mock entire modules. There are many different possiblities. Go through the Jest documentation for mocking [here](https://jestjs.io/docs/mock-functions) and [here](https://jestjs.io/docs/mock-function-api).
121+
122+
## Mock Data
123+
124+
Sometimes in our tests we may need large sets of mock data, such as mock names, mock addresses, mock phone numbers or mock credit card numbers depending on our application. It can be difficult to come up with such mock values but it's also not advisable to use too trivial values like "test", "123" or "lorem ipsum".
125+
126+
In such cases, we can use libraries like [faker](https://www.npmjs.com/package/faker) which helps generate different kinds of fake/mock data which looks like real data.
127+
128+
Some examples are:
129+
```js
130+
var faker = require('faker');
131+
var randomName = faker.name.findName(); // Rowan Nikolaus
132+
var randomEmail = faker.internet.email(); // [email protected]
133+
var randomCard = faker.helpers.createCard(); // random contact card containing many properties
134+
```
135+
136+
It also has localization settings to generate fake data in many different languages.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Test Coverage
2+
The objective of this lesson is to get a general idea about test coverage and its importance.
3+
4+
## What is test coverage?
5+
6+
Test coverage is defined as a metric in that measures the amount of testing performed by a set of tests. It includes gathering information about which parts of the codebase are executed when running the test suite to determine which branches of conditional statements have been taken.
7+
8+
In simple terms, it is a technique to ensure that your tests are testing your code or how much of your code you exercised by running the test.
9+
10+
If you had to manually calculate test coverage, here's a simple example:
11+
12+
If the number of lines of code in a system component is 500 and the number of lines executed across all existing test cases is 50, then your test coverage is: `(50 / 500) * 100 = 10%`
13+
14+
However, depending on the codebase size and structure, the formula can get more complicated.
15+
16+
## Why should we care about test coverage?
17+
18+
1. **Eliminates defects at early stages**: You can identify gaps in requirements, test cases and defects at early stages of your product development life cycle. It saves you from a lot of headaches later.
19+
20+
2. **Better coverage**: Test coverage creates more test cases to ensure superior coverage. This leads to fewer defects and work to do at later stages. Moreover, you get to increase customer satisfaction with a refined product.
21+
22+
3. **Removes redundant cases**: Test coverage is especially useful in identifying and eliminating test cases that don't make much sense in the current project. You can report these cases to remove them and make the overall code lighter.
23+
24+
4. **Discovers uncovered areas**: Test coverage helps you unearth areas of a program that have not been covered by a set of test cases. It helps make your program more robust and error-free.
25+
26+
5. **Superior control**: Test coverage gives you a better control over the resources during the product development lifecycle. You save time by eliminating defects earlier and faster. The saved time allows you to keep a tab of costs. And most importantly, you get to have a firm grip on the scope of the project.
27+
28+
6. **Smoother testing cycles**: You can prevent defect leakage using Test coverage analysis. Test coverage also helps in Regression testing, test case prioritization, test suite augmentation and test suite minimization. All this leads to smoother yet efficient testing cycles.
29+
30+
## What is a good test coverage?
31+
32+
It is absolutely not necessary to have 100% test coverage. Suppose your application has 50 features, and that when you run your tests, they exercise only 37 of those features. In this case, we could say that your tests cover 74% of the application. But if you can argue that the remaining 13 features are not critical, then 74% is still a good test coverage.
33+
34+
It usually depends on the type of application, its functional and non-functional requirements, time and effort and number of developers required to write tests - all together that help a company decide the right amount of test coverage for them.
35+
36+
In fact, sometimes you may even find that checking the test coverage leads to finding less important test cases and that might lead to reducing the test cases to get the right balance.
37+
38+
## How to measure test coverage?
39+
40+
If you're using Jest, you can simply run the command `jest --coverage` to get a tabular report of the code coverage of your project files. [Here](https://medium.com/@blturner3527/code-coverage-and-testing-with-jest-9641b5d0e0bc) is a simple tutorial about the same.
41+
42+
You can also use a tool called [Istanbul](https://istanbul.js.org/) which works with the Mocha framework to generate code coverage reports.
43+
44+
---
45+
## References
46+
- https://www.guru99.com/test-coverage-in-software-testing.html
47+
- https://www.simform.com/blog/test-coverage/

0 commit comments

Comments
 (0)