Skip to content

Commit

Permalink
Merge pull request #2 from nearform/first-review-fix
Browse files Browse the repository at this point in the history
First review fix
  • Loading branch information
alfonsograziano committed Feb 13, 2024
2 parents 24b2aff + 6c70938 commit bc2d0bf
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 8 deletions.
123 changes: 115 additions & 8 deletions slides.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
</div>

Expand All @@ -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.
</div>

---
Expand All @@ -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 a series of exercises
- At each step you're asked to use a different test runner feature
- The 💡 icon indicates hints

Expand Down Expand Up @@ -539,17 +539,124 @@ You can reference the [`--import` official documentation](https://nodejs.org/api

---

# Other useful resources
# A11 Timers

<div class="dense">

- 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.

</div>

---

# Thanks For Having Us!
# A11 The problem

## 👏👏👏
- 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

````
---

# 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()
})
```

---

# A12 Context

<div class="dense">

- 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.

</div>

---

# 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

---

# Thanks For Having Us!

## 👏👏👏
7 changes: 7 additions & 0 deletions src/a11-timers/README.md
Original file line number Diff line number Diff line change
@@ -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`.
14 changes: 14 additions & 0 deletions src/a11-timers/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
5 changes: 5 additions & 0 deletions src/a11-timers/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function delayedHello(callback, delay) {
setTimeout(() => {
callback('Hello, World!')
}, delay)
}
24 changes: 24 additions & 0 deletions src/a11-timers/test/index.test.js
Original file line number Diff line number Diff line change
@@ -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
})
20 changes: 20 additions & 0 deletions src/a11-timers/test/solution.test.js
Original file line number Diff line number Diff line change
@@ -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].arguments[0], 'Hello, World!')

mock.timers.reset()
})
7 changes: 7 additions & 0 deletions src/a12-context/README.md
Original file line number Diff line number Diff line change
@@ -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`.
14 changes: 14 additions & 0 deletions src/a12-context/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
20 changes: 20 additions & 0 deletions src/a12-context/src/index.js
Original file line number Diff line number Diff line change
@@ -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
}
31 changes: 31 additions & 0 deletions src/a12-context/test/index.test.js
Original file line number Diff line number Diff line change
@@ -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'
})
})
37 changes: 37 additions & 0 deletions src/a12-context/test/solution.test.js
Original file line number Diff line number Diff line change
@@ -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'
})
})
})

0 comments on commit bc2d0bf

Please sign in to comment.