mirror of
https://github.com/kou029w/nodejs-hands-on.git
synced 2025-01-18 16:08:05 +00:00
create test
This commit is contained in:
parent
8592a7a559
commit
d38b654d8b
35 changed files with 687 additions and 1 deletions
38
package-lock.json
generated
38
package-lock.json
generated
|
@ -8,6 +8,9 @@
|
||||||
"name": "nodejs-hands-on",
|
"name": "nodejs-hands-on",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
|
"workspaces": [
|
||||||
|
"templates/*"
|
||||||
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"markdown-link-check": "^3.11.2",
|
"markdown-link-check": "^3.11.2",
|
||||||
"prettier": "^3.0.0"
|
"prettier": "^3.0.0"
|
||||||
|
@ -19,6 +22,10 @@
|
||||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
|
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/basic": {
|
||||||
|
"resolved": "templates/basic",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/boolbase": {
|
"node_modules/boolbase": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
|
@ -326,6 +333,10 @@
|
||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mock": {
|
||||||
|
"resolved": "templates/mock",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
@ -409,6 +420,10 @@
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/promise": {
|
||||||
|
"resolved": "templates/promise",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
|
@ -429,6 +444,29 @@
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
|
||||||
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==",
|
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/scope": {
|
||||||
|
"resolved": "templates/scope",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
|
"node_modules/template": {
|
||||||
|
"resolved": "templates/template",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
|
"templates/basic": {
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"templates/mock": {
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"templates/promise": {
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"templates/scope": {
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"templates/template": {
|
||||||
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,13 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"author": "Kohei Watanabe <kou029w@gmail.com>",
|
"author": "Kohei Watanabe <kou029w@gmail.com>",
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
|
"type": "module",
|
||||||
|
"workspaces": [
|
||||||
|
"templates/*"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "git config core.hooksPath .githooks",
|
"prepare": "git config core.hooksPath .githooks",
|
||||||
"test": "find src -name '*.md' -exec markdown-link-check {} +",
|
"test": "find src -name '*.md' -exec markdown-link-check {} + && node --test src && npm test --workspaces",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -6,3 +6,11 @@
|
||||||
- [VSCodeのインストール](installing-vscode.md)
|
- [VSCodeのインストール](installing-vscode.md)
|
||||||
- [VSCodeの拡張機能](vscode-extensions.md)
|
- [VSCodeの拡張機能](vscode-extensions.md)
|
||||||
- [VSCodeでのNode.jsのデバッグ](debugging-nodejs-in-vscode.md)
|
- [VSCodeでのNode.jsのデバッグ](debugging-nodejs-in-vscode.md)
|
||||||
|
- [Test Runnerではじめるテスト](test/index.md)
|
||||||
|
- [事前準備](test/preparation.md)
|
||||||
|
- [はじめてのテスト](test/getting-started.md)
|
||||||
|
- [基本的な機能](test/basic.md)
|
||||||
|
- [テストの実践 ――「うるう年」問題](test/practices.md)
|
||||||
|
- [テストの作法](test/writing-style.md)
|
||||||
|
- [参考文献・動画](test/references.md)
|
||||||
|
- [質問・提案・問題の報告](issues.md)
|
||||||
|
|
3
src/issues.md
Normal file
3
src/issues.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# 質問・提案・問題の報告
|
||||||
|
|
||||||
|
もし何か気になることがあれば、[GitHub Issues](https://github.com/kou029w/nodejs-hands-on/issues/new) からお気軽にお寄せください。
|
48
src/test/basic.md
Normal file
48
src/test/basic.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# 基本的な機能
|
||||||
|
|
||||||
|
Node.jsのテストランナーの機能について説明します。
|
||||||
|
|
||||||
|
## テストファイルの検出
|
||||||
|
|
||||||
|
デフォルトで下記のパターンに一致するすべてのファイルをテストファイルとして検出します[^running-tests-from-the-command-line]。
|
||||||
|
|
||||||
|
- `**/*.test.?(c|m)js`
|
||||||
|
- `**/*-test.?(c|m)js`
|
||||||
|
- `**/*_test.?(c|m)js`
|
||||||
|
- `**/test-*.?(c|m)js`
|
||||||
|
- `**/test.?(c|m)js`
|
||||||
|
- `**/test/**/*.?(c|m)js`
|
||||||
|
|
||||||
|
[^running-tests-from-the-command-line]: <https://nodejs.org/api/test.html#running-tests-from-the-command-line>
|
||||||
|
|
||||||
|
## テストの自動監視
|
||||||
|
|
||||||
|
`--watch` オプションを指定することで、テストファイルの変更を自動で監視します。
|
||||||
|
|
||||||
|
```
|
||||||
|
node --test --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
終了するにはキーボードの <ruby>`Ctrl` + `C`<rt>`Ctrl`キーを押しながら`C`</rt></ruby> を押します。
|
||||||
|
|
||||||
|
## プロジェクトでのテストコマンドの設定
|
||||||
|
|
||||||
|
この設定を行うと、`npm test` コマンドでテストを実行できるようになります。
|
||||||
|
|
||||||
|
`package.json` の `scripts` プロパティの中を下記のように変更します。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
NPMコマンドでのテストの実行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
`node --test` コマンドの実行と同様のテスト結果が得られます。
|
111
src/test/getting-started.md
Normal file
111
src/test/getting-started.md
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
# はじめてのテスト
|
||||||
|
|
||||||
|
テストランナーは、`node --test` コマンドを使用することで実行できます。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --test
|
||||||
|
```
|
||||||
|
|
||||||
|
しかし、まだテストが1件も存在しません。
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ node --test
|
||||||
|
ℹ tests 0
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 0
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 1.92388
|
||||||
|
```
|
||||||
|
|
||||||
|
実際にテストを作成し、実行していきましょう。
|
||||||
|
|
||||||
|
## ECMAScriptモジュール
|
||||||
|
|
||||||
|
ECMAScriptモジュール (ESM) とは、JavaScriptをモジュールとして再利用できるようにするための仕組みです。
|
||||||
|
|
||||||
|
Node.jsでESMを取り扱えるようにするためには `package.json` ファイルに `"type": "module"` プロパティを加えます。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
このように書き加えると、プロジェクトの `.js` ファイルはESMとして取り扱われます。
|
||||||
|
|
||||||
|
## テストファイルの作成
|
||||||
|
|
||||||
|
次のファイルを作成します。
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include getting-started/hello.test.js}}
|
||||||
|
```
|
||||||
|
|
||||||
|
この作成した `hello.test.js` は、`node --test` コマンドを実行するときにテストとして実行されるようになります。
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ node --test
|
||||||
|
✔ 1と2の合計は3です (0.546922ms)
|
||||||
|
ℹ tests 1
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 1
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 65.244086
|
||||||
|
```
|
||||||
|
|
||||||
|
問題なく実行できましたか?
|
||||||
|
|
||||||
|
```
|
||||||
|
✔ 1と2の合計は3です (0.546922ms)
|
||||||
|
```
|
||||||
|
|
||||||
|
画面に表示されたこの部分は「テスト "1と2の合計は3です" が実行され、そのテストは合格しました ✅」ということを意味しています。
|
||||||
|
|
||||||
|
このようにしてNode.jsで簡単にテストを行うことができます。
|
||||||
|
|
||||||
|
## はじめてのテストのコードの説明
|
||||||
|
|
||||||
|
テストのコードについてより詳しく説明します。
|
||||||
|
|
||||||
|
はじめてのテストのコード:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include getting-started/hello.test.js}}
|
||||||
|
```
|
||||||
|
|
||||||
|
このコードは、「1と2の合計は3です」というテストを意味します。
|
||||||
|
式 `1 + 2` が、 `3` と等しいことを検証するテストです。
|
||||||
|
|
||||||
|
このコードでは下記の機能が使われています。
|
||||||
|
|
||||||
|
`test()` 関数
|
||||||
|
|
||||||
|
テストを宣言するための関数です。
|
||||||
|
|
||||||
|
- 第一引数には、このテストの説明を人間が読める形式で記述します
|
||||||
|
- 第二引数には、テストの本体を記述します
|
||||||
|
|
||||||
|
`assert.strictEqual()` 関数
|
||||||
|
|
||||||
|
引数に与えた値を検証します。
|
||||||
|
|
||||||
|
`assert.strictEqual(<検査される値>, <期待する値>)`
|
||||||
|
|
||||||
|
「検査される値」と「期待する値」の同一性を検証します。
|
||||||
|
|
||||||
|
最初の行は `import` 文によって `node:assert` と `node:test` を指定しています。
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include getting-started/hello.test.js:2:3}}
|
||||||
|
```
|
||||||
|
|
||||||
|
これらがNode.jsのテストランナーの実行に必要となります。
|
||||||
|
|
||||||
|
このコードは基本的な機能を確認するための極めて単純なテストですが、テスト環境自体の検証を行うことでもあります。
|
||||||
|
テスト環境の検証は、テストを行う上で最初に確認しておく重要なポイントです。
|
7
src/test/getting-started/hello.test.js
Normal file
7
src/test/getting-started/hello.test.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// hello.test.js
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
test("1と2の合計は3です", () => {
|
||||||
|
assert.strictEqual(1 + 2, 3);
|
||||||
|
});
|
19
src/test/index.md
Normal file
19
src/test/index.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Test Runnerではじめるテスト
|
||||||
|
|
||||||
|
Node.jsに組み込まれた新しいテストランナーを使ってテストを行う入門ガイドです。
|
||||||
|
Node.jsでどうやってテストするんだろう?という疑問に答えます。
|
||||||
|
テストを行っていくための最初の一歩になればと思います。
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
src="https://stackblitz.com/github/kou029w/nodejs-hands-on/tree/main/templates/template?embed=1&view=editor&terminal=watch&file=sum.js,sum.test.js"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
height: 640px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
"
|
||||||
|
title="template"
|
||||||
|
></iframe>
|
||||||
|
|
||||||
|
それでは、さっそく学んでいきましょう!
|
164
src/test/practices.md
Normal file
164
src/test/practices.md
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
# テストの実践 ――「うるう年」問題
|
||||||
|
|
||||||
|
ここでは「うるう年」を判定するモジュールを作成します。
|
||||||
|
「うるう年」の判定は、通常広く使われている `date-fns` などのNPMパッケージを使用することが多いですが、ここではテストを学ぶためにあえて自分で実装します。
|
||||||
|
設計して、テストを書き、コードを書くという一連のステップでより実践的なテストとの付き合い方を学びましょう。
|
||||||
|
|
||||||
|
## ECMAScriptモジュール
|
||||||
|
|
||||||
|
ECMAScriptモジュール (ESM) とは、JavaScriptをモジュールとして再利用できるようにするための仕組みです。
|
||||||
|
|
||||||
|
Node.jsでESMを取り扱えるようにするためには `package.json` ファイルに `"type": "module"` プロパティを加えます。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
このように書き加えると、プロジェクトの `.js` ファイルはESMとして取り扱われます。
|
||||||
|
|
||||||
|
## 目標の決定
|
||||||
|
|
||||||
|
まず「何を作るか」明らかにしましょう。
|
||||||
|
何を作るか曖昧なまま、ただ無為にソフトウェア開発を進めるとムダを生む恐れがあります。
|
||||||
|
ムダを生まないためにできるだけ「何を作るか」を明確にしておきましょう。
|
||||||
|
|
||||||
|
「うるう年」を判定するということは、「西暦年号がうるう年ならば true を返し、そうでなければ false を返す関数」ということと決めます。
|
||||||
|
|
||||||
|
「うるう年」とは何であるかは、ここでは日本の法令を参考にして決めます。
|
||||||
|
日本の法令上の取り扱いは、明治時代に制定された「閏年ニ関スル件」によって決められています。
|
||||||
|
|
||||||
|
> 神武天皇即位紀元年数ノ四ヲ以テ整除シ得ヘキ年ヲ閏年トス但シ紀元年数ヨリ六百六十ヲ減シテ百ヲ以テ整除シ得ヘキモノノ中更ニ四ヲ以テ商ヲ整除シ得サル年ハ平年トス
|
||||||
|
>
|
||||||
|
> ―― [https://elaws.e-gov.go.jp/document?lawid=131IO0000000090](https://elaws.e-gov.go.jp/document?lawid=131IO0000000090)
|
||||||
|
|
||||||
|
「神武天皇即位紀元」は、通常の西暦年号でいう紀元前660年を元年とした暦を意味します。したがって「紀元年数ヨリ六百六十ヲ減シテ」とあるのは通常の西暦年号を意味します。
|
||||||
|
端的に言うと「グレゴリオ暦法に基づいています」ということを意味します。
|
||||||
|
このままだと大変読みにくいですね。
|
||||||
|
書き換えると下記のようになります。
|
||||||
|
|
||||||
|
> 西暦年号が4で割り切れる年はうるう年。ただし、西暦年号が100で割り切れる年のうち、100で割った商が4で割り切れない年はうるう年ではない。
|
||||||
|
|
||||||
|
これを「うるう年」とします。整理するとこうなります。
|
||||||
|
|
||||||
|
- 西暦年号が4で割り切れる年はうるう年
|
||||||
|
- たとえば、西暦2024年、2028年、2032年は4で割り切れるので、うるう年です。
|
||||||
|
- 西暦年号が4で割り切れない年はうるう年でない
|
||||||
|
- たとえば、西暦2021年、2022年、2023年は4で割り切れないので、うるう年ではありません。
|
||||||
|
- ただし、西暦年号が100で割り切れる年はうるう年でない
|
||||||
|
- たとえば、西暦2100年、2200年、2300年は100で割り切れるので、うるう年ではありません。
|
||||||
|
- ただし、西暦年号が400で割り切れる年はうるう年
|
||||||
|
- たとえば、西暦2000年、2400年、2800年は400で割り切れるので、うるう年です。
|
||||||
|
|
||||||
|
これで「何を作るか」ということが明らかになりました。
|
||||||
|
それでは、順番にテストとコードを書いていきましょう。
|
||||||
|
|
||||||
|
## 「西暦年号が4で割り切れる年はうるう年」
|
||||||
|
|
||||||
|
ファイル `isLeapYear.test.js` を作成します。
|
||||||
|
「何を作るか」ということを忘れないようにコメントに転載します。
|
||||||
|
|
||||||
|
```js
|
||||||
|
// isLeapYear.test.js
|
||||||
|
/** TODO:
|
||||||
|
西暦年号が4で割り切れる年はうるう年
|
||||||
|
たとえば、西暦2024年、2028年、2032年は4で割り切れるので、うるう年です。
|
||||||
|
西暦年号が4で割り切れない年はうるう年でない
|
||||||
|
たとえば、西暦2021年、2022年、2023年は4で割り切れないので、うるう年ではありません。
|
||||||
|
ただし、西暦年号が100で割り切れる年はうるう年でない
|
||||||
|
たとえば、西暦2100年、2200年、2300年は100で割り切れるので、うるう年ではありません。
|
||||||
|
ただし、西暦年号が400で割り切れる年はうるう年
|
||||||
|
たとえば、西暦2000年、2400年、2800年は400で割り切れるので、うるう年です。
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
うるう年であることを判定するので `isLeapYear` という名前に決めました。
|
||||||
|
この名前のモジュールと関数を作成することに決めます。
|
||||||
|
|
||||||
|
テストを書いていきましょう。
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include practices/step0.test.js:1:3}}
|
||||||
|
{{#include practices/step0.test.js:5:}}
|
||||||
|
```
|
||||||
|
|
||||||
|
これをテストし、失敗することを確認します。
|
||||||
|
この失敗は、テスト環境自体の検証を行うことでもあります。
|
||||||
|
|
||||||
|
NPMコマンドでのテストの実行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
テストの自動監視:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --test --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
テスト結果:
|
||||||
|
|
||||||
|
```console
|
||||||
|
✖ 西暦年号が4で割り切れる年はうるう年
|
||||||
|
```
|
||||||
|
|
||||||
|
失敗しますね。
|
||||||
|
この失敗によって、次の2点を実証できました。
|
||||||
|
|
||||||
|
- 目標の「西暦年号が4で割り切れる年はうるう年」というテストが実行されること
|
||||||
|
- 未実装のコードが**意図せず合格しない ❌** ということ
|
||||||
|
|
||||||
|
テスト環境の検証は、テストを行う上での重要なポイントです。
|
||||||
|
|
||||||
|
それでは、関数を実装していきましょう。
|
||||||
|
最初からすべての実装を書こうとせず、小さい変更のみで済ませるのがポイントです。
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include practices/isLeapYear.js}}
|
||||||
|
```
|
||||||
|
|
||||||
|
ファイルを作成したら、テスト側で `import` 文によって実装した関数を読み込みます。
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include practices/step0.test.js}}
|
||||||
|
```
|
||||||
|
|
||||||
|
テストを実行します。
|
||||||
|
|
||||||
|
テスト結果:
|
||||||
|
|
||||||
|
```console
|
||||||
|
✔ 西暦年号が4で割り切れる年はうるう年
|
||||||
|
```
|
||||||
|
|
||||||
|
これでテストは合格 ✅ しました。
|
||||||
|
念の為、西暦2024年のケースだけでなくほかのケースもテストしてみましょう。
|
||||||
|
|
||||||
|
「西暦年号が4で割り切れる年はうるう年」という目標を達成したと判断したら、コメントからは消しておきます。
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include practices/step1.test.js}}
|
||||||
|
```
|
||||||
|
|
||||||
|
次の目標「西暦年号が4で割り切れない年はうるう年でない」に進めていきます。
|
||||||
|
|
||||||
|
テストを書き、実行します。
|
||||||
|
|
||||||
|
必要に応じて実装を修正します。
|
||||||
|
|
||||||
|
これらのテストも問題なく合格するようになれば、「西暦年号が4で割り切れない年はうるう年でない」という目標も達成したと判断して、コメントから消しておきます。
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include practices/step2.test.js}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 続きの課題
|
||||||
|
|
||||||
|
残りの目標に関しても同様に進めていきましょう。
|
||||||
|
|
||||||
|
- ただし、西暦年号が100で割り切れる年はうるう年でない
|
||||||
|
- たとえば、西暦2100年、2200年、2300年は100で割り切れるので、うるう年ではありません。
|
||||||
|
- ただし、西暦年号が400で割り切れる年はうるう年
|
||||||
|
- たとえば、西暦2000年、2400年、2800年は400で割り切れるので、うるう年です。
|
6
src/test/practices/isLeapYear.js
Normal file
6
src/test/practices/isLeapYear.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// isLeapYear.js
|
||||||
|
function isLeapYear(year) {
|
||||||
|
return year % 4 === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isLeapYear;
|
8
src/test/practices/step0.test.js
Normal file
8
src/test/practices/step0.test.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// isLeapYear.test.js
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
import isLeapYear from "./isLeapYear.js";
|
||||||
|
|
||||||
|
test("西暦年号が4で割り切れる年はうるう年", () => {
|
||||||
|
assert.strictEqual(isLeapYear(2024), true);
|
||||||
|
});
|
19
src/test/practices/step1.test.js
Normal file
19
src/test/practices/step1.test.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// isLeapYear.test.js
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
import isLeapYear from "./isLeapYear.js";
|
||||||
|
|
||||||
|
test("西暦年号が4で割り切れる年はうるう年", () => {
|
||||||
|
assert.strictEqual(isLeapYear(2024), true);
|
||||||
|
assert.strictEqual(isLeapYear(2028), true);
|
||||||
|
assert.strictEqual(isLeapYear(2032), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
/** TODO:
|
||||||
|
西暦年号が4で割り切れない年はうるう年でない
|
||||||
|
たとえば、西暦2021年、2022年、2023年は4で割り切れないので、うるう年ではありません。
|
||||||
|
ただし、西暦年号が100で割り切れる年はうるう年でない
|
||||||
|
たとえば、西暦2100年、2200年、2300年は100で割り切れるので、うるう年ではありません。
|
||||||
|
ただし、西暦年号が400で割り切れる年はうるう年
|
||||||
|
たとえば、西暦2000年、2400年、2800年は400で割り切れるので、うるう年です。
|
||||||
|
*/
|
23
src/test/practices/step2.test.js
Normal file
23
src/test/practices/step2.test.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// isLeapYear.test.js
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
import isLeapYear from "./isLeapYear.js";
|
||||||
|
|
||||||
|
test("西暦年号が4で割り切れる年はうるう年", () => {
|
||||||
|
assert.strictEqual(isLeapYear(2024), true);
|
||||||
|
assert.strictEqual(isLeapYear(2028), true);
|
||||||
|
assert.strictEqual(isLeapYear(2032), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("西暦年号が4で割り切れない年はうるう年でない", () => {
|
||||||
|
assert.strictEqual(isLeapYear(2021), false);
|
||||||
|
assert.strictEqual(isLeapYear(2022), false);
|
||||||
|
assert.strictEqual(isLeapYear(2023), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
/** TODO:
|
||||||
|
ただし、西暦年号が100で割り切れる年はうるう年でない
|
||||||
|
たとえば、西暦2100年、2200年、2300年は100で割り切れるので、うるう年ではありません。
|
||||||
|
ただし、西暦年号が400で割り切れる年はうるう年
|
||||||
|
たとえば、西暦2000年、2400年、2800年は400で割り切れるので、うるう年です。
|
||||||
|
*/
|
17
src/test/preparation.md
Normal file
17
src/test/preparation.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# 事前準備
|
||||||
|
|
||||||
|
あらかじめNode.jsの実行環境を構築してからはじめます。
|
||||||
|
|
||||||
|
## StackBlitzではじめる
|
||||||
|
|
||||||
|
次のリンクにアクセスすると、StackBlitzで新しいNode.jsの実行環境を構築できます。
|
||||||
|
|
||||||
|
[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/node?view=editor)
|
||||||
|
|
||||||
|
StackBlitzではじめる場合は、以降の準備は不要です。
|
||||||
|
|
||||||
|
## ローカル環境ではじめる
|
||||||
|
|
||||||
|
ローカル環境にNode.jsの実行環境を構築する場合、まずはじめにNode.jsをインストールします。
|
||||||
|
インストール方法は[Node\.jsのインストール \- Node\.jsを使う](../installing-nodejs.md)をご参照ください。
|
||||||
|
プロジェクトの作成方法は[package.jsonファイル - Node.jsを使う](../package-json.md)をご参照ください。
|
34
src/test/references.md
Normal file
34
src/test/references.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# 参考文献・動画
|
||||||
|
|
||||||
|
## Node.js
|
||||||
|
|
||||||
|
- [Node.js 公式ドキュメント](https://nodejs.org/api/test.html)
|
||||||
|
|
||||||
|
## テスト駆動開発
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
src="https://www.slideshare.net/slideshow/embed_code/key/GcQ7SzVAQNOpWU"
|
||||||
|
style="
|
||||||
|
max-width: 595px;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 595 / 485;
|
||||||
|
border: 0;
|
||||||
|
"
|
||||||
|
allow="fullscreen"
|
||||||
|
></iframe>
|
||||||
|
|
||||||
|
[和田卓人 (2010)「TDD のこころ」](https://www.slideshare.net/t_wada/the-spirit-of-tdd)
|
||||||
|
|
||||||
|
[和田卓人 (2020)「TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング」](https://www.youtube.com/watch?v=Q-FJ3XmFlT8)
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
src="https://www.youtube-nocookie.com/embed/vrbMKbdV6xY"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 560 / 315;
|
||||||
|
border: 0;
|
||||||
|
"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
|
||||||
|
[安井力 (2021)「『テスト自動化とテスト駆動開発』講演動画」](https://www.youtube.com/watch?v=vrbMKbdV6xY)
|
15
src/test/writing-style.md
Normal file
15
src/test/writing-style.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# テストの作法
|
||||||
|
|
||||||
|
テストを書くときの代表的な作法を紹介します。
|
||||||
|
|
||||||
|
## Arrange・Act・Assert (AAA) パターン
|
||||||
|
|
||||||
|
テストを書くときの作法の1つです。
|
||||||
|
準備 (Arrange)・実行 (Act)・検証 (Assert) というプロセスで分けて書きます。
|
||||||
|
準備・実行・検証をそれぞれ分けて書いておくことで比較的読みやすいテストを書くことができます。
|
||||||
|
|
||||||
|
例:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{#include writing-style/aaa-pattern.test.js}}
|
||||||
|
```
|
13
src/test/writing-style/aaa-pattern.test.js
Normal file
13
src/test/writing-style/aaa-pattern.test.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
test("正しくJSONをパースできる", () => {
|
||||||
|
// 準備
|
||||||
|
const json = `{ "name": "Claude Monet", "birth": "1840" }`;
|
||||||
|
|
||||||
|
// 実行
|
||||||
|
const parsed = JSON.parse(json);
|
||||||
|
|
||||||
|
// 検証
|
||||||
|
assert.deepStrictEqual(parsed, { name: "Claude Monet", birth: "1840" });
|
||||||
|
});
|
1
templates/basic/main.js
Normal file
1
templates/basic/main.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// toBe.test.js, toEqual.test.js, notToBe.test.js をご参照ください
|
6
templates/basic/notToBe.test.js
Normal file
6
templates/basic/notToBe.test.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
test("2と2の和は5ではない", () => {
|
||||||
|
assert.notStrictEqual(2 + 2, 5);
|
||||||
|
});
|
11
templates/basic/package.json
Normal file
11
templates/basic/package.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "basic",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "main.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --test",
|
||||||
|
"watch": "node --test --watch"
|
||||||
|
}
|
||||||
|
}
|
6
templates/basic/toBe.test.js
Normal file
6
templates/basic/toBe.test.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
test("2と2の和は4", () => {
|
||||||
|
assert.strictEqual(2 + 2, 4);
|
||||||
|
});
|
8
templates/basic/toEqual.test.js
Normal file
8
templates/basic/toEqual.test.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
test("Object.assign()でプロパティを代入できる", () => {
|
||||||
|
const data = { name: "Claude Monet" };
|
||||||
|
Object.assign(data, { birth: "1840" });
|
||||||
|
assert.deepStrictEqual(data, { name: "Claude Monet", birth: "1840" });
|
||||||
|
});
|
1
templates/mock/main.js
Normal file
1
templates/mock/main.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// mock.test.js をご参照ください
|
14
templates/mock/mock.test.js
Normal file
14
templates/mock/mock.test.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
const mockCallback = test.mock.fn();
|
||||||
|
|
||||||
|
["い", "ろ", "は"].forEach(mockCallback);
|
||||||
|
|
||||||
|
test("関数は3回呼ばれます", () => {
|
||||||
|
assert.strictEqual(mockCallback.mock.calls.length, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("最初の呼び出しのときの第1引数は「い」です", () => {
|
||||||
|
assert.strictEqual(mockCallback.mock.calls[0].arguments[0], "い");
|
||||||
|
});
|
11
templates/mock/package.json
Normal file
11
templates/mock/package.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "mock",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "main.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --test",
|
||||||
|
"watch": "node --test --watch"
|
||||||
|
}
|
||||||
|
}
|
1
templates/promise/main.js
Normal file
1
templates/promise/main.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// promise.test.js をご参照ください
|
11
templates/promise/package.json
Normal file
11
templates/promise/package.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "promise",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "main.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --test",
|
||||||
|
"watch": "node --test --watch"
|
||||||
|
}
|
||||||
|
}
|
9
templates/promise/promise.test.js
Normal file
9
templates/promise/promise.test.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
const fetchData = () => Promise.resolve(42);
|
||||||
|
|
||||||
|
test("データは42", async () => {
|
||||||
|
const data = await fetchData();
|
||||||
|
assert.strictEqual(data, 42);
|
||||||
|
});
|
1
templates/scope/main.js
Normal file
1
templates/scope/main.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// scope.test.js をご参照ください
|
11
templates/scope/package.json
Normal file
11
templates/scope/package.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "scope",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "main.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --test",
|
||||||
|
"watch": "node --test --watch"
|
||||||
|
}
|
||||||
|
}
|
34
templates/scope/scope.test.js
Normal file
34
templates/scope/scope.test.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import assert from "node:assert";
|
||||||
|
import { after, afterEach, before, beforeEach, describe, it } from "node:test";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 実行順序:
|
||||||
|
* トップ - before → グループ1 - before
|
||||||
|
* → トップ - beforeEach → グループ1 - beforeEach
|
||||||
|
* → グループ1 - test 1
|
||||||
|
* → トップ - afterEach → グループ1 - afterEach
|
||||||
|
* → トップ - beforeEach → グループ1 - beforeEach
|
||||||
|
* → グループ1 - test 2
|
||||||
|
* → トップ - afterEach → グループ1 - afterEach
|
||||||
|
* → グループ1 - after → トップ - after
|
||||||
|
*/
|
||||||
|
|
||||||
|
before(() => console.log("トップ - before"));
|
||||||
|
after(() => console.log("トップ - after"));
|
||||||
|
beforeEach(() => console.log("トップ - beforeEach"));
|
||||||
|
afterEach(() => console.log("トップ - afterEach"));
|
||||||
|
|
||||||
|
describe("グループ1", () => {
|
||||||
|
before(() => console.log("グループ1 - before"));
|
||||||
|
after(() => console.log("グループ1 - after"));
|
||||||
|
beforeEach(() => console.log("グループ1 - beforeEach"));
|
||||||
|
afterEach(() => console.log("グループ1 - afterEach"));
|
||||||
|
|
||||||
|
it("グループ1 - test 1", () => {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
it("グループ1 - test 2", () => {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
});
|
1
templates/template/main.js
Normal file
1
templates/template/main.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// https://kou029w.github.io/nodejs-hands-on/test/ をご参照ください
|
11
templates/template/package.json
Normal file
11
templates/template/package.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "template",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "main.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --test",
|
||||||
|
"watch": "node --test --watch"
|
||||||
|
}
|
||||||
|
}
|
5
templates/template/sum.js
Normal file
5
templates/template/sum.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
function sum(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default sum;
|
7
templates/template/sum.test.js
Normal file
7
templates/template/sum.test.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
import sum from "./sum.js";
|
||||||
|
|
||||||
|
test("1と2の合計は3です", () => {
|
||||||
|
assert.strictEqual(sum(1, 2), 3);
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue