Skip to content

Commit c7fa174

Browse files
authored
add video embeds (#6)
1 parent 8058cdc commit c7fa174

File tree

49 files changed

+157
-55
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+157
-55
lines changed
Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,5 @@
11
# Test boundaries
22

3-
You will be testing all sorts of code in the wild. Some of it will be simple and self-contained, and some will be tangled with dependencies and side effects. Test boundaries is what helps you to untangle more complex code, focusing on the exact [intention](https://www.epicweb.dev/the-true-purpose-of-testing) you wish to test.
3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/boundaries/test-boundaries" />
44

5-
To help you visualize what mocking does to your tested code I built a little app called [How Mocking Works](https://howmocking.works). Let's explore it together as a part of this exercise.
6-
7-
<callout-info>**You don't have to code anything in this exercise**. There's plenty of coding stored for you later! For now, let's have a proper introduction into mocking with a bit of visual learning.</callout-info>
8-
9-
![How Mocking Works?](https://howmocking.works/og.jpg)
10-
11-
## Mocking visualized
12-
13-
In the app, I represented various JavaScript modules, like `one.js` and `two.js` with geometric shapes, while the dependencies between them with colored lines. The graph that you see represents the extent of the tested code if you are writing a test for the `one.js` module.
14-
15-
By default, _all of the `one.js` dependencies run in the test_. By clicking on the links (lines) between various modules and their methods you can "cut off" (or mock) everything that goes past that link.
16-
17-
The orange double-line injected into the dependency link represents a _mock_. It can be anything, from intercepting an HTTP call to mocking the method or the entire module. Those are the technical details of the mock. No matter what mocking technique you use, it ends up trimming your dependency tree to the scope you need in your test.
18-
19-
## Overmocking
20-
21-
Mocking is a powerful tool. It is crucial you learn from the start that it has to be used sparingly and with a clear purpose. If misused, it can result in tests that don't actually test anything or make your testing experience far more difficult than it has to be.
22-
23-
There are a couple of rules to keep your mocking superpowers at bay:
24-
25-
### Never mock anything directly related to the intention you want to test
26-
27-
If you are testing what happens when the user clicks a button, you mustn't mock that button's behavior. If you do, your test will be testing your mock, and that's not what you want.
28-
29-
> You can reproduce this scenario visually by putting a mock between the `one.js` module and its dependency on the `a()` method. Nothing will be tested if you do!
30-
31-
It may not always be as obvious to determine what's related to the tested intention and what's not. For example, when testing your `Emitter.prototype.emit` method to make sure it calls the previously added listeners, it may be tempting to assume that the listener that's being called is related to the intention.
32-
33-
```ts
34-
const emitter = new Emitter()
35-
emitter.on('event', listener)
36-
emitter.emit('event')
37-
// Make sure the "listener" function is called!
38-
```
39-
40-
But the `listener` function itself is not the point. In fact, it can be anything. Its implementation does not influence what you want to test—that _any_ previously added listener gets called when the `event` is emitted.
41-
42-
> :scroll: Follow the [Golden Rule of Assertions](https://www.epicweb.dev/the-golden-rule-of-assertions) to see what influences the result of your test and what doesn't.
43-
44-
### Always mock third-party HTTP calls
45-
46-
No matter the testing level, you must always omit the HTTP calls to _third-party services_ from your test suite. They may seem important but they never are. Your tests can still focus on whether _your application_ performs the necessary requests to those services but their operability must not influence the result of your tests.
47-
48-
> You do not own third-party systems and as such, they mustn't be allowed to affect your test.
49-
50-
### Choose the least intrusive mocking technique possible
51-
52-
In other words, draw the most permissive test boundary that you can.
53-
54-
Coming back to our visual example, if you want to exclude `b()` from the test, mock the `two.js` [dependency on `b()`](https://howmocking.works/#7) and not [the entire `two.js` module](https://howmocking.works/#2). By putting the mock too "high" up the dependency tree, you may lose the important behaviors of your code even if they don't seem related to what you are testing. But most likely, you would end up re-implementing the missing pieces because now _you_ become in charge of the entire `two.js` module.
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 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.
Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,56 @@
11
# Test boundaries
22

3-
I like the start topics 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.
3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/boundaries/test-boundaries/solution" />
4+
5+
You will be testing all sorts of code in the wild. Some of it will be simple and self-contained, and some will be tangled with dependencies and side effects. Test boundaries is what helps you to untangle more complex code, focusing on the exact [intention](https://www.epicweb.dev/the-true-purpose-of-testing) you wish to test.
6+
7+
To help you visualize what mocking does to your tested code I built a little app called [How Mocking Works](https://howmocking.works). Let's explore it together as a part of this exercise.
8+
9+
<callout-info>**You don't have to code anything in this exercise**. There's plenty of coding stored for you later! For now, let's have a proper introduction into mocking with a bit of visual learning.</callout-info>
10+
11+
![How Mocking Works?](https://howmocking.works/og.jpg)
12+
13+
## Mocking visualized
14+
15+
In the app, I represented various JavaScript modules, like `one.js` and `two.js` with geometric shapes, while the dependencies between them with colored lines. The graph that you see represents the extent of the tested code if you are writing a test for the `one.js` module.
16+
17+
By default, _all of the `one.js` dependencies run in the test_. By clicking on the links (lines) between various modules and their methods you can "cut off" (or mock) everything that goes past that link.
18+
19+
The orange double-line injected into the dependency link represents a _mock_. It can be anything, from intercepting an HTTP call to mocking the method or the entire module. Those are the technical details of the mock. No matter what mocking technique you use, it ends up trimming your dependency tree to the scope you need in your test.
20+
21+
## Overmocking
22+
23+
Mocking is a powerful tool. It is crucial you learn from the start that it has to be used sparingly and with a clear purpose. If misused, it can result in tests that don't actually test anything or make your testing experience far more difficult than it has to be.
24+
25+
There are a couple of rules to keep your mocking superpowers at bay:
26+
27+
### Never mock anything directly related to the intention you want to test
28+
29+
If you are testing what happens when the user clicks a button, you mustn't mock that button's behavior. If you do, your test will be testing your mock, and that's not what you want.
30+
31+
> You can reproduce this scenario visually by putting a mock between the `one.js` module and its dependency on the `a()` method. Nothing will be tested if you do!
32+
33+
It may not always be as obvious to determine what's related to the tested intention and what's not. For example, when testing your `Emitter.prototype.emit` method to make sure it calls the previously added listeners, it may be tempting to assume that the listener that's being called is related to the intention.
34+
35+
```ts
36+
const emitter = new Emitter()
37+
emitter.on('event', listener)
38+
emitter.emit('event')
39+
// Make sure the "listener" function is called!
40+
```
41+
42+
But the `listener` function itself is not the point. In fact, it can be anything. Its implementation does not influence what you want to test—that _any_ previously added listener gets called when the `event` is emitted.
43+
44+
> :scroll: Follow the [Golden Rule of Assertions](https://www.epicweb.dev/the-golden-rule-of-assertions) to see what influences the result of your test and what doesn't.
45+
46+
### Always mock third-party HTTP calls
47+
48+
No matter the testing level, you must always omit the HTTP calls to _third-party services_ from your test suite. They may seem important but they never are. Your tests can still focus on whether _your application_ performs the necessary requests to those services but their operability must not influence the result of your tests.
49+
50+
> You do not own third-party systems and as such, they mustn't be allowed to affect your test.
51+
52+
### Choose the least intrusive mocking technique possible
53+
54+
In other words, draw the most permissive test boundary that you can.
55+
56+
Coming back to our visual example, if you want to exclude `b()` from the test, mock the `two.js` [dependency on `b()`](https://howmocking.works/#7) and not [the entire `two.js` module](https://howmocking.works/#2). By putting the mock too "high" up the dependency tree, you may lose the important behaviors of your code even if they don't seem related to what you are testing. But most likely, you would end up re-implementing the missing pieces because now _you_ become in charge of the entire `two.js` module.

exercises/01.boundaries/FINISHED.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Boundaries
22

3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/boundaries/recap-of-boundaries" />
4+
35
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.
46

57
Ultimately, it is _your_ decision when to draw that line. Just know that it will affect what kind of test you get in return.

exercises/01.boundaries/README.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Boundaries
22

3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/boundaries/intro-to-boundaries" />
4+
35
There has been almost two decades since mocks (originally called "_test doubles_") were first defined. If you go and search for "mocks" online right now, you will find a plethora of technical explanations and usage examples on how to substitute, spy, stub, replace, fake, and do all sorts of things with the tested code. Most of those resources put emphasis on different ways to mock but, somehow, seldom bother to explain what _is_ a mock, _when_ would you reach out for one, and _what_ it actually does to your code.
46

57
## What is mocking?

exercises/02.functions/01.problem.mock-functions/README.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Mock functions
22

3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/functions/mock-functions" />
4+
35
We have an `Emitter` implementation that can add listeners to events and call those listeners whenever a matching event is emitted. This behavior is achieved by implementing two core methods on the emitter: `.on()` and `.emit()`:
46

57
<CodeFile file="emitter.ts" range="7-22" nocopy />

exercises/02.functions/01.solution.mock-functions/README.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Mock functions
22

3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/functions/mock-functions/solution" />
4+
35
In the `emitter.test.ts`, I will start from creating the `listener` function but it won't be a regular JavaScript function. Instead, I will use the `vi.fn()` API from Vitest, which creates a _special_ kind of function.
46

57
<CodeFile file="emitter.test.ts" range="5" />

exercises/02.functions/02.problem.spies/README.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Spies
22

3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/functions/spies" />
4+
35
Testing the intentions around side effects is always tricky. Like this `UserService` class that's supposed to log out the `createUser` event whenever a new user is created:
46

57
<CodeFile file="user-service.ts" highlight="7" nocopy />

exercises/02.functions/02.solution.spies/README.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Spies
22

3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/functions/spies/solution" />
4+
35
Let's start by creating a _spy_ for the `logger.log()` method using the [`vi.spyOn()`](https://vitest.dev/api/vi.html#vi-spyon) function from Vitest:
46

57
<CodeFile file="user-service.test.ts" range="5-7" highlight="6" />

exercises/02.functions/03.problem.mock-implementation/README.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Mock implementation
22

3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/functions/mock-implementation" />
4+
35
In this one, we have an `OrderController` class responsible for handling orders in our eshop. It has a `.createOrder()` method that accepts a `cart` object and does the following:
46

57
1. Throws an error if any of the cart items are out of stock;

exercises/02.functions/03.solution.mock-implementation/README.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Mock implementation
22

3+
<EpicVideo url="https://www.epicweb.dev/workshops/mocking-techniques-in-vitest/functions/mock-implementation/solution" />
4+
35
Let's start by going to the first test case for our `OrderController` and spying on the `isItemInStock` method of the created `controller` instance:
46

57
<CodeFile file="order-controller.test.ts" range="3-6" highlight="6" />

0 commit comments

Comments
 (0)