You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I like to start with seemingly basic and straightforward exercises because they end up growing in value as you progress through the material (and beyond). The test boundary is one of such examples. In the end, the entire purpose of mocking is to draw that boundary, and you cannot wield mocking efficiently without understanding it first.
5
+
I like to start with seemingly basic and straightforward exercises because they end up growing in value as you progress through the material (and beyond). The test boundary is one such example. In the end, the entire purpose of mocking is to draw that boundary, and you cannot wield mocking efficiently without first understanding it.
Copy file name to clipboardExpand all lines: exercises/01.boundaries/FINISHED.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,13 +4,13 @@
4
4
5
5
Understanding test boundaries helps you think about mocking as a tool. A tool that helps you balance what matters in the test vs what doesn't. That's really what mocking of any kind is about.
6
6
7
-
Ultimately, it is _your_ decision when to draw that line. Just know that it will affect what kind of test you get in return.
7
+
Ultimately, it is _your_ decision where to draw that line. Just know that it will affect what kind of test you get in return.
8
8
9
9
In the next couple of hours, you will learn about different mocking techniques in JavaScript to help you draw that line more efficiently and with bigger confidence. Let's go!
10
10
11
11
## Related materials
12
12
13
-
Here are a few materials that can help you develop the right conception of mocking:
13
+
Here are a few materials that can help you develop the right mental model of mocking:
14
14
15
15
-[What Is a Test Boundary?](https://www.epicweb.dev/what-is-a-test-boundary)
This creates an _integration test_ where the connection between `one()` and `two()` is important. This test will fail if `one()` behaves unexpectedly. This test will also fail if `two()` happened to do the same, despite being the test for the `one()` function.
35
35
36
-
This behavior is neither good nor bad. You are testing the code as-is, and the code has a dependency on another code. If you do nothing about that dependency, you will bring it into the test as well, which is precisely what you want for an integration test!
36
+
This behavior is neither good nor bad. You are testing the code as-is, and the code has a dependency on some other code. If you do nothing about that dependency, you will bring it into the test as well, which is precisely what you want for an integration test!
37
37
38
38
Sometimes, however, you need to test certain behaviors in isolation. Sometimes, you need to make dependencies behave a certain way to put your tested code in the right state. In those situations, you need to establish a test boundary.
39
39
@@ -65,7 +65,7 @@ return result * 2
65
65
// ^^^
66
66
```
67
67
68
-
Because everything else in `one()` is now _mocked_. The test will no longer fail if `two()` has a bug, only if `one()` fails to do the multiplication correctly.
68
+
Because everything else in `one()` is now _mocked_, the test will no longer fail if `two()` has a bug — it will only fail if `one()` fails to do the multiplication correctly.
69
69
70
70
> You use mocking to preserve the dependency but lift its behavior from the code to the test, making it controllable.
71
71
@@ -99,15 +99,15 @@ But also, perhaps the most important question: What does mocking do to my code?
99
99
100
100
## What mocking does to your code
101
101
102
-
Mocking is an essential tool for test testing. It can be your lever to transition between different testing levels, handle dependencies, model required behaviors, and focus on particular logic (not to mention various mocking applications outside of the realm of testing!). But beware: when mocking, **you are modifying your code**.
102
+
Mocking is an essential tool for testing. It can be your lever to transition between different testing levels, handle dependencies, model required behaviors, and focus on particular logic (not to mention various mocking applications outside of the realm of testing!). But beware: when mocking, **you are modifying your code**.
103
103
104
104
<callout-infoclass="important">
105
105
The more you alter the system under test, the more you are testing a different
106
106
system.
107
107
</callout-info>
108
108
109
-
Any mock you introduce creates a tiny alternative universe where the code you are testing is mostly the same but not quite. Those "not quite"s can be desired and controllable, but they can also decrease the quality of your tests and make you trust them less. Like any tool, mocking has to be handled with purpose and care.
109
+
Any mock you introduce creates a tiny parallel universe where the code you are testing is mostly the same but not quite. Those "not quite"s can be desired and controllable, but they can also decrease the quality of your tests and make you trust them less. Like any tool, mocking has to be handled with purpose and care.
110
110
111
-
You don't want to end up in the state where you are running tests against mocks alone. If that happens, you aren't testing anything and should take a good dozen of steps back to reconsider your testing strategy.
111
+
You don't want to end up in the state where you are running tests against mocks alone. If that happens, you aren't testing anything and should take a good step back and reconsider your testing strategy.
112
112
113
113
In this workshop, we will take a look at the most common applications of mocking and also when and how to use those. But before we begin, let's make sure you get the hang of the concept of a "test boundary" first.
This means we can use a _dependency injection_ to provide it with whichever logger instance we want in test. For example, a logger that we will 🕵️ _spy on_.
36
36
37
-
_Spying_ is a technique to record a function's execution. It's like pressing a record button on a radio to listen to any function calls while it's being used. Spies give us access to the information like the number of function calls, the exact arguments provided, and values returned. Spies also allow us to completely override the behavior of any function, if we want to.
37
+
_Spying_ is a technique to record a function's execution. It's like pressing a record button on a radio to listen to any function calls while it's being used. Spies give us access to information like the number of function calls, the exact arguments provided, and the values returned. Spies also allow us to completely override the behavior of any function, if we want to.
38
38
39
39
You will use both recording and overriding in this exercise.
Copy file name to clipboardExpand all lines: exercises/02.functions/03.problem.mock-implementation/README.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -50,6 +50,6 @@ export class OrderController {
50
50
51
51
That's the responsibility Peter bestowed upon himself.
52
52
53
-
Your responsibility, however, is to test the `.createOrder()` method on **the**`controller` class. There are couple of ways you can use mocking to help you with that, especially when it comes to modeling different item availability states.
53
+
Your responsibility, however, is to test the `.createOrder()` method on the`OrderController` class. There are a couple of ways you can use mocking to help you with that, especially when it comes to modeling different item availability states.
54
54
55
55
👨💼 Write automated tests for _both_ of the expectations toward the `.createOrder()` method by using [`.mockReturnValue()`](https://vitest.dev/api/mock.html#mockreturnvalue) and [`.mockImplementation()`](https://vitest.dev/api/mock.html#mockimplementation) utilities in Vitest to control the behavior of the `.isItemInStock()` method. By the end of the exercise, `npm test` must pass!
Functions is the heart and soul of any logic in JavaScript. Event handlers, callbacks, utilities—you have likely worked with functions before. Whenever you want your code to _do_ something you reach out to a function because it represents _action_.
5
+
Functions are the heart and soul of any logic in JavaScript. Event handlers, callbacks, utilities—you have likely worked with functions before. Whenever you want your code to _do_ something you reach out to a function because it represents _action_.
6
6
7
7
Ideally, testing functions should come down to the I/O (input/output) testing. You provide a function with the right input and assert the expected output. This testing strategy is extremely prevalent in unit testing and it truly shines with pure functions.
8
8
9
-
But the thing is, functions, much like the intentions behind them, can be different. Not all functions you will write will be pure. Some will have side effects, some will be asynchronous, the others will introduce dependencies or non-deterministic behaviors. There may also be times when the function itself wouldn't matter, and instead you would want to test if it's being called or not.
9
+
But the thing is, functions, much like the intentions behind them, can be different. Not all functions you will write will be pure. Some will have side effects, some will be asynchronous, and others will introduce dependencies or non-deterministic behaviors. There may also be times when the function itself doesn't matter, and instead you would want to test if it's being called or not.
10
10
11
-
There are plenty of cases to reach out to mocking when testing functions, and it's time you learned how to do it.
11
+
There are plenty of cases where mocking is useful when testing functions, and it’s time you learned how to do it.
12
12
13
13
## Mocking functions
14
14
15
-
Mocking imbues your functions with superpowers. There are multiple techniques of mocking functions, and they all come down to the following points:
15
+
Mocking imbues your functions with superpowers. There are multiple techniques for mocking functions, and they all come down to the following points:
16
16
17
-
1. Recording the function calls;
18
-
1. Replacing the function's implementation;
17
+
1. Recording function calls;
18
+
1. Replacing function implementations;
19
19
1. Creating placeholder functions to use as arguments.
20
20
21
-
In this exercise block, you will learn how to wield all three and also see the use cases for different kinds of function mocks. Let's begin!
21
+
In this exercise block, you will learn how to wield all three and explore use cases for different types of function mocks. Let's begin!
Copy file name to clipboardExpand all lines: exercises/03.date-and-time/01.problem.date-time/README.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,7 +43,7 @@ export function getRelativeTime(date: Date): string {
43
43
44
44
> We are also using the standard [`Intl.RelativeTimeFormat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat) API to help us format the time: "1 minute ago" but "5 minute<u>s</u> ago". Give it a read if you're not familiar with it!
45
45
46
-
By using mocking, we can become magicians who can bend time and space! Well, maybe just time, for now. Vitest comes with a built-in [`vi.useFakeTimers()`](https://vitest.dev/api/vi.html#vi-usefaketimers) utility to make otherwise unpredictable date and time fixed and given.
46
+
By using mocking, we can become magicians who can bend time and space! Well, maybe just time—for now. Vitest comes with a built-in [`vi.useFakeTimers()`](https://vitest.dev/api/vi.html#vi-usefaketimers) utility to make otherwise unpredictable date and time fixed and given.
47
47
48
48
Since mocking time is such a common use case, Vitest gives you a more comfortable API to work with while also allowing to toggle between the "fake" and the "real" date whenever you need.
// will resolve to the midnight of the 1st of June 2024.
58
58
```
59
59
60
-
But this is, of course, not enough. We don't need a specific date but _different_ days to model relative scenarios (1 minute ago, 2 months ago, etc). Loos like each test will exist in its own, separate little universe!
60
+
But this is, of course, not enough. We don't need a specific date but _different_ days to model relative scenarios (1 minute ago, 2 months ago, etc). Each test will exist in its own separate little universe!
61
61
62
62
👨💼 Complete the test suite for the `getRelativeTime()` function. Remember that `vi.useFakeTimers()` is still a mock and must be treated as such (cleanup?). Verify your solution by running `npm test`.
Copy file name to clipboardExpand all lines: exercises/03.date-and-time/01.solution.date-time/README.mdx
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@
4
4
5
5
You may be noticing a pattern here. I'm trying to keep mock definitions contained and use the hooks like `beforeAll()` and `afterAll()` to make sure no mocks are left once the tests are done (and, often, even between the tests).
6
6
7
-
Testing the `getRelativeTime()` function will be no exception:
7
+
Testing the `getRelativeTime()` function is no exception:
Calling `vi.useFakeTimers()`detached this test from the real flow of time, and makes it rely on the internal fake date Vitest has. Correspondingly, the `vi.useRealTimers()` utility undoes that.
19
+
Calling `vi.useFakeTimers()`detaches this test from the real flow of time, making it rely on the internal fake date Vitest has. Correspondingly, the `vi.useRealTimers()` utility undoes that.
20
20
21
21
To fix date and time in test, I will call `vi.setSystemTime()` function and provide it with the date that I want to be treated as `Date.now()`:
> This produces the time delta of `0ms`, which results in the `"Just now"` string being returned from the function.
37
37
38
-
For the tests that feature an actual time difference, I have to use a slightly different setup. Firsts, the system time (which is `Date.now()`) should be set to the time that has _already passed_. Then, the `getRelativeTime()` function will accept the _starting time_ as an argument.
38
+
For the tests that feature an actual time difference, I have to use a slightly different setup. First, the system time (which is `Date.now()`) should be set to the time that has _already passed_. Then, the `getRelativeTime()` function will accept the _starting time_ as an argument.
[Debounce and throttle](https://kettanaito.com/blog/debounce-vs-throttle) have to be some of my favorite utility functions, and they both happened to rely on timers to work. Let's study them in more detail and also see how we would test them.
5
+
[Debounce and throttle](https://kettanaito.com/blog/debounce-vs-throttle) have to be some of my favorite utility functions, and they both happen to rely on timers to work. Let's study them in more detail and also see how we would test them.
6
6
7
7
Take a look at the `debounce` function implementation below:
I begin from setting up the testing hooks to mock time in this test, using the `vi.useFakeTimers()` and `vi.useRealTimers()` functions, the same way I did in the previous exercise:
5
+
I begin by setting up the testing hooks to mock time in this test, using the `vi.useFakeTimers()` and `vi.useRealTimers()` functions, the same way I did in the previous exercise:
The second test case will employ everything we've learned thus far to test the repetitive calls to the debounced function:
57
+
The second test case will use everything we've learned so far to test repeated calls to the debounced function:
58
58
59
59
```tsfilename=debounce.test.tsnonumberlines=8,13
60
60
test('debounces the callback until the timeout passes since the last call', () => {
@@ -78,7 +78,7 @@ test('debounces the callback until the timeout passes since the last call', () =
78
78
79
79
> Note that you can use fake timers and `vi.advanceTimersByTime()` and `vi.advanceTimersToNextTimer()` to test intervals (`setInterval`) too!
80
80
81
-
## Difference with`sleep()`
81
+
## Difference from`sleep()`
82
82
83
83
You may be wondering about the difference between `vi.advanceTimersByTime()` and something like a `sleep()` function. Since in this case we do need to wait for a fixed period of time to pass, using `sleep()` may seem like a viable alternative. But the two behave completely differently.
0 commit comments