From 254654aa3b2275d56ac1334f4501c8eb99ed3b9d Mon Sep 17 00:00:00 2001 From: Alfonso Graziano Date: Fri, 9 Feb 2024 07:40:34 +0100 Subject: [PATCH 1/8] Mention Jest instead of Mocha --- slides.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slides.md b/slides.md index b73a20d..e33a771 100644 --- a/slides.md +++ b/slides.md @@ -47,7 +47,7 @@ Test runners can be part of a larger **test framework** or standalone tools. - Test runners are tools designed to execute your test suites and report the results. They are essential in automating the testing process. - Python: `pytest` is widely appreciated for its powerful features and simple syntax, making it suitable for both simple and complex projects. - Java: `JUnit` is the de facto standard for unit testing in Java development, known for its rich annotation-based configuration. -- JavaScript: `Mocha` is a flexible test framework with a focus on asynchronous testing, offering rich features for running tests in Node.js and the browser. +- JavaScript: `Jest` is a flexible test framework with a focus on asynchronous testing, offering rich features for running tests in Node.js and the browser. - .NET: `NUnit` is a popular choice for .NET developers, similar to JUnit but with a focus on the .NET framework. @@ -62,7 +62,7 @@ Choosing the right test runner involves considering the programming language, pr - Test Runner: A tool that executes tests and reports the results. It is responsible for loading your test code, running it, and then providing feedback. - Testing Framework: Provides the structure and guidelines for writing tests. It includes assertions, test cases, and test suites, but doesn't run tests by itself. - The main difference lies in their roles; while a testing framework defines how to write tests, a test runner actually executes them. -- Some tools, like `pytest` and `Mocha`, combine both functionalities, acting as both test runners and frameworks. +- Some tools, like `pytest` and `Jest`, combine both functionalities, acting as both test runners and frameworks. --- From 87177cfa1ed9c4f149ecc7d6de83e5a4f908c0d2 Mon Sep 17 00:00:00 2001 From: Alfonso Graziano Date: Fri, 9 Feb 2024 07:44:35 +0100 Subject: [PATCH 2/8] Don't show number of exercises in the slide --- slides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides.md b/slides.md index e33a771..0935ec2 100644 --- a/slides.md +++ b/slides.md @@ -89,7 +89,7 @@ Choosing the right test runner involves considering the programming language, pr # Workshop setup -- This workshop will introduce to the Node.js test runner with 11 excercises +- This workshop will introduce to the Node.js test runner with some exercises - At each step you're asked to use a different test runner feature - The 💡 icon indicates hints From 0ffeacdb845694bcacc0dbf5c9d6e1c3bd76747e Mon Sep 17 00:00:00 2001 From: Alfonso Graziano Date: Fri, 9 Feb 2024 08:45:37 +0100 Subject: [PATCH 3/8] New chapter - timers --- slides.md | 50 ++++++++++++++++++++++++---- src/a11-timers/README.md | 7 ++++ src/a11-timers/package.json | 14 ++++++++ src/a11-timers/src/index.js | 5 +++ src/a11-timers/test/index.test.js | 24 +++++++++++++ src/a11-timers/test/solution.test.js | 20 +++++++++++ 6 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 src/a11-timers/README.md create mode 100644 src/a11-timers/package.json create mode 100644 src/a11-timers/src/index.js create mode 100644 src/a11-timers/test/index.test.js create mode 100644 src/a11-timers/test/solution.test.js diff --git a/slides.md b/slides.md index 0935ec2..71079c9 100644 --- a/slides.md +++ b/slides.md @@ -539,17 +539,55 @@ You can reference the [`--import` official documentation](https://nodejs.org/api --- -# Other useful resources +# A11 Timers + +
+ +- Timers are crucial for testing **time-dependent functionality** in applications, such as debouncing, throttling, or any operation that relies on time delays. +- Using real timers in tests can lead to unpredictable results and slow down the testing process, as tests have to wait for the actual time to pass. +- The Node.js test runner offers a way to **mock timers**, enabling tests to simulate the passage of time instantly. +- Developers can enable mocked versions of timers like `setTimeout` and `setInterval` that can be controlled programmatically. + +
--- -# Thanks For Having Us! +# A11 The problem -## 👏👏👏 +- In the `test` folder, there is a `index.test.ts` file +- The function to test, contains a `setTimeout` +- During testing, this can lead to slow and unpredictable tests +- Apply [timers mocking](https://nodejs.org/api/test.html#timers) in the test file + +--- -```` +# A11 Solution 💡 -``` +```javascript +test('delayedHello executes the callback after the specified delay', () => { + const fn = mock.fn() + mock.timers.enable({ apis: ['setTimeout'] }) + delayedHello(fn, 5000) + + // Initially, the callback has not been called + assert.strictEqual(fn.mock.calls.length, 0) + // Advance time by 5000 milliseconds + mock.timers.tick(5000) + // Now, the callback should have been called once + assert.strictEqual(fn.mock.calls.length, 1) + assert.strictEqual(fn.mock.calls[0][0], 'Hello, World!') + + mock.timers.reset() +}) ``` -```` + +--- + +# Other useful resources + +--- + +# Thanks For Having Us! + +## 👏👏👏 diff --git a/src/a11-timers/README.md b/src/a11-timers/README.md new file mode 100644 index 0000000..f6ef1ba --- /dev/null +++ b/src/a11-timers/README.md @@ -0,0 +1,7 @@ +# a11-timers + +- Open the file `test/index.test.js`. + +- Use the [Timers API](https://nodejs.org/api/test.html#timers) to mock the `setTimeout` + +- Run in the terminal `node --test`. diff --git a/src/a11-timers/package.json b/src/a11-timers/package.json new file mode 100644 index 0000000..0ef2230 --- /dev/null +++ b/src/a11-timers/package.json @@ -0,0 +1,14 @@ +{ + "name": "a11-timers", + "version": "1.0.0", + "description": "", + "type": "module", + "main": "index.js", + "scripts": { + "test": "node --test", + "solution": "node --test ./test/solution.test.js" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/src/a11-timers/src/index.js b/src/a11-timers/src/index.js new file mode 100644 index 0000000..9799d9d --- /dev/null +++ b/src/a11-timers/src/index.js @@ -0,0 +1,5 @@ +export function delayedHello(callback, delay) { + setTimeout(() => { + callback('Hello, World!') + }, delay) +} diff --git a/src/a11-timers/test/index.test.js b/src/a11-timers/test/index.test.js new file mode 100644 index 0000000..6ce971c --- /dev/null +++ b/src/a11-timers/test/index.test.js @@ -0,0 +1,24 @@ +/* eslint-disable no-unused-vars */ +import assert from 'node:assert' +import { delayedHello } from '../src/index.js' +import { test, mock } from 'node:test' + +test('delayedHello executes the callback after the specified delay', () => { + const fn = mock.fn() + + // Enable mocking of setTimeout + + // Call the function with a mock callback and a delay + //delayedHello(fn, 5000) + + // Initially, the callback has not been called + //assert.strictEqual(fn.mock.calls.length, 0) + + // Advance time by 5000 milliseconds + + // Now, the callback should have been called once + //assert.strictEqual(fn.mock.calls.length, 1) + //assert.strictEqual(fn.mock.calls[0][0], 'Hello, World!') + + // Reset mock timers after the test +}) diff --git a/src/a11-timers/test/solution.test.js b/src/a11-timers/test/solution.test.js new file mode 100644 index 0000000..53599ff --- /dev/null +++ b/src/a11-timers/test/solution.test.js @@ -0,0 +1,20 @@ +import assert from 'node:assert' +import { delayedHello } from '../src/index.js' +import { test, mock } from 'node:test' + +test('delayedHello executes the callback after the specified delay', () => { + const fn = mock.fn() + + mock.timers.enable({ apis: ['setTimeout'] }) + delayedHello(fn, 5000) + + // Initially, the callback has not been called + assert.strictEqual(fn.mock.calls.length, 0) + // Advance time by 5000 milliseconds + mock.timers.tick(5000) + // Now, the callback should have been called once + assert.strictEqual(fn.mock.calls.length, 1) + assert.strictEqual(fn.mock.calls[0][0], 'Hello, World!') + + mock.timers.reset() +}) From 634d15cd117b17dd40471abd4bb62d4d233284c8 Mon Sep 17 00:00:00 2001 From: Alfonso Graziano Date: Fri, 9 Feb 2024 09:36:50 +0100 Subject: [PATCH 4/8] A-12 context and subtests --- slides.md | 69 +++++++++++++++++++++++++++ src/a12-context/README.md | 7 +++ src/a12-context/package.json | 14 ++++++ src/a12-context/src/index.js | 20 ++++++++ src/a12-context/test/index.test.js | 31 ++++++++++++ src/a12-context/test/solution.test.js | 37 ++++++++++++++ 6 files changed, 178 insertions(+) create mode 100644 src/a12-context/README.md create mode 100644 src/a12-context/package.json create mode 100644 src/a12-context/src/index.js create mode 100644 src/a12-context/test/index.test.js create mode 100644 src/a12-context/test/solution.test.js diff --git a/slides.md b/slides.md index 71079c9..4bed5fd 100644 --- a/slides.md +++ b/slides.md @@ -584,6 +584,75 @@ test('delayedHello executes the callback after the specified delay', () => { --- +# A12 Context + +
+ +- The `context` object is essential for managing test lifecycles, including setup and teardown processes. +- It provides hooks (`before`, `beforeEach`, `after`, `afterEach`) for preparing and cleaning up before and after tests or a group of tests. +- Enables control over test execution through methods like `skip` (to bypass tests), `todo` (to mark tests as pending), and `runOnly` (to execute only specified tests). +- Offers a `diagnostic` method for logging debug information and a signal property for aborting tests programmatically. +- Supports **hierarchical test structuring** with the test method, allowing for the creation of subtests that inherit the context of their parent test. +- Facilitates grouping related tests by using beforeEach and afterEach hooks for shared setup and cleanup, ensuring a well-organized and maintainable test suite. + +
+ +--- + +# A12 The problem + +- In this exercise about context, we will focus on child tests (also known as subtests) +- In the file `index.test.js` you will find multiple tests for the `sum` and the `average` functions +- Group together all the subtests related to the same function + +--- + +# A12 Solution 💡 + +```javascript +// Grouping tests for `sum` function +test('sum function tests', async t => { + await t.test('Sum works correctly with valid input', () => { + assert.deepStrictEqual(sum([1, 2, 3]), 6) + }) + + await t.test('Sum returns 0 in case of empty array', () => { + assert.deepStrictEqual(sum([]), 0) + }) + + await t.test('Sum throws in case of bad input', () => { + assert.throws(() => sum('abc'), { + message: 'Input must be an array of numbers' + }) + }) +}) +``` + +--- + +# A12 Solution 💡 (2) + +```javascript +// Grouping tests for `average` function +test('average function tests', async t => { + await t.test('Average works correctly with valid input', () => { + assert.deepStrictEqual(average([1, 2, 3]), 2) + }) + + await t.test('Average returns 0 in case of empty array', () => { + assert.deepStrictEqual(average([]), 0) + }) + + await t.test('Average throws in case of bad input', () => { + assert.throws(() => average('abc'), { + message: 'Input must be an array of numbers' + }) + }) +}) +``` + +--- + # Other useful resources --- diff --git a/src/a12-context/README.md b/src/a12-context/README.md new file mode 100644 index 0000000..5f92736 --- /dev/null +++ b/src/a12-context/README.md @@ -0,0 +1,7 @@ +# a12-context + +- Open the file `test/index.test.js`. + +- Group all the subtests of a specific function with a context + +- Run in the terminal `node --test`. diff --git a/src/a12-context/package.json b/src/a12-context/package.json new file mode 100644 index 0000000..ca14aa7 --- /dev/null +++ b/src/a12-context/package.json @@ -0,0 +1,14 @@ +{ + "name": "a12-context", + "version": "1.0.0", + "description": "", + "type": "module", + "main": "index.js", + "scripts": { + "test": "node --test", + "solution": "node --test ./test/solution.test.js" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/src/a12-context/src/index.js b/src/a12-context/src/index.js new file mode 100644 index 0000000..ea97b09 --- /dev/null +++ b/src/a12-context/src/index.js @@ -0,0 +1,20 @@ +export function sum(numbers) { + if (!Array.isArray(numbers)) { + throw new Error('Input must be an array of numbers') + } + + return numbers.reduce((acc, num) => acc + num, 0) +} + +export function average(numbers) { + if (!Array.isArray(numbers)) { + throw new Error('Input must be an array of numbers') + } + + if (numbers.length === 0) { + return 0 + } + + const sum = numbers.reduce((acc, num) => acc + num, 0) + return sum / numbers.length +} diff --git a/src/a12-context/test/index.test.js b/src/a12-context/test/index.test.js new file mode 100644 index 0000000..e1744f1 --- /dev/null +++ b/src/a12-context/test/index.test.js @@ -0,0 +1,31 @@ +import { average, sum } from '../src/index.js' +import { test } from 'node:test' +import assert from 'node:assert' + +test('Sum works correctly with valid input', () => { + assert.deepStrictEqual(sum([1, 2, 3]), 6) +}) + +test('Sum returns 0 in case of empty array', () => { + assert.deepStrictEqual(sum([]), 0) +}) + +test('Sum throws in case of bad input', () => { + assert.throws(() => sum('abc'), { + message: 'Input must be an array of numbers' + }) +}) + +test('Average works correctly with valid input', () => { + assert.deepStrictEqual(average([1, 2, 3]), 2) +}) + +test('Average returns 0 in case of empty array', () => { + assert.deepStrictEqual(average([]), 0) +}) + +test('Average throws in case of bad input', () => { + assert.throws(() => average('abc'), { + message: 'Input must be an array of numbers' + }) +}) diff --git a/src/a12-context/test/solution.test.js b/src/a12-context/test/solution.test.js new file mode 100644 index 0000000..5c188cc --- /dev/null +++ b/src/a12-context/test/solution.test.js @@ -0,0 +1,37 @@ +import { average, sum } from '../src/index.js' +import { test } from 'node:test' +import assert from 'node:assert' + +// Grouping tests for `sum` function +test('sum function tests', async t => { + await t.test('Sum works correctly with valid input', () => { + assert.deepStrictEqual(sum([1, 2, 3]), 6) + }) + + await t.test('Sum returns 0 in case of empty array', () => { + assert.deepStrictEqual(sum([]), 0) + }) + + await t.test('Sum throws in case of bad input', () => { + assert.throws(() => sum('abc'), { + message: 'Input must be an array of numbers' + }) + }) +}) + +// Grouping tests for `average` function +test('average function tests', async t => { + await t.test('Average works correctly with valid input', () => { + assert.deepStrictEqual(average([1, 2, 3]), 2) + }) + + await t.test('Average returns 0 in case of empty array', () => { + assert.deepStrictEqual(average([]), 0) + }) + + await t.test('Average throws in case of bad input', () => { + assert.throws(() => average('abc'), { + message: 'Input must be an array of numbers' + }) + }) +}) From c289cf3e2b9d804d8283f5bf29caaffbe579c552 Mon Sep 17 00:00:00 2001 From: Alfonso Graziano Date: Fri, 9 Feb 2024 10:48:25 +0100 Subject: [PATCH 5/8] Update slides.md Co-authored-by: Marco Ippolito --- slides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides.md b/slides.md index 4bed5fd..7cf93e3 100644 --- a/slides.md +++ b/slides.md @@ -89,7 +89,7 @@ Choosing the right test runner involves considering the programming language, pr # Workshop setup -- This workshop will introduce to the Node.js test runner with some exercises +- This workshop will introduce to the Node.js test runner with a series of exercises - At each step you're asked to use a different test runner feature - The 💡 icon indicates hints From a56f49fc44a82da43c7a6207946b06c2eaf50649 Mon Sep 17 00:00:00 2001 From: Alfonso Graziano Date: Fri, 9 Feb 2024 10:53:03 +0100 Subject: [PATCH 6/8] Fix typo --- slides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides.md b/slides.md index 7cf93e3..57ef4f8 100644 --- a/slides.md +++ b/slides.md @@ -554,7 +554,7 @@ You can reference the [`--import` official documentation](https://nodejs.org/api # A11 The problem -- In the `test` folder, there is a `index.test.ts` file +- In the `test` folder, there is a `index.test.js` file - The function to test, contains a `setTimeout` - During testing, this can lead to slow and unpredictable tests - Apply [timers mocking](https://nodejs.org/api/test.html#timers) in the test file From 15f5d359e7d9613e4b939f920dd6cd61329ca094 Mon Sep 17 00:00:00 2001 From: Alfonso Graziano Date: Fri, 9 Feb 2024 10:55:07 +0100 Subject: [PATCH 7/8] Fix backticks in hooks --- slides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides.md b/slides.md index 57ef4f8..7445180 100644 --- a/slides.md +++ b/slides.md @@ -593,7 +593,7 @@ test('delayedHello executes the callback after the specified delay', () => { - Enables control over test execution through methods like `skip` (to bypass tests), `todo` (to mark tests as pending), and `runOnly` (to execute only specified tests). - Offers a `diagnostic` method for logging debug information and a signal property for aborting tests programmatically. - Supports **hierarchical test structuring** with the test method, allowing for the creation of subtests that inherit the context of their parent test. -- Facilitates grouping related tests by using beforeEach and afterEach hooks for shared setup and cleanup, ensuring a well-organized and maintainable test suite. +- Facilitates grouping related tests by using `beforeEach` and `afterEach` hooks for shared setup and cleanup, ensuring a well-organized and maintainable test suite. From 6c7093887ef4a80ae210aa6cf2319b98ebf6df60 Mon Sep 17 00:00:00 2001 From: Alfonso Graziano Date: Fri, 9 Feb 2024 10:59:13 +0100 Subject: [PATCH 8/8] Fix assertion --- src/a11-timers/test/solution.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/a11-timers/test/solution.test.js b/src/a11-timers/test/solution.test.js index 53599ff..4e42773 100644 --- a/src/a11-timers/test/solution.test.js +++ b/src/a11-timers/test/solution.test.js @@ -14,7 +14,7 @@ test('delayedHello executes the callback after the specified delay', () => { mock.timers.tick(5000) // Now, the callback should have been called once assert.strictEqual(fn.mock.calls.length, 1) - assert.strictEqual(fn.mock.calls[0][0], 'Hello, World!') + assert.strictEqual(fn.mock.calls[0].arguments[0], 'Hello, World!') mock.timers.reset() })