Skip to content

Commit d238bba

Browse files
committed
12章まで追加、requireが毎回再読み込みするようにする
1 parent f0b8818 commit d238bba

File tree

5 files changed

+576
-1
lines changed

5 files changed

+576
-1
lines changed

app/pagesList.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export const pagesList = [
3535
{ id: 8, title: "モジュールとMix-in" },
3636
{ id: 9, title: "Proc, Lambda, クロージャ" },
3737
{ id: 10, title: "標準ライブラリの活用" },
38+
{ id: 11, title: "テスト文化入門" },
39+
{ id: 12, title: "メタプログラミング入門" },
3840
],
3941
},
4042
{

public/docs/ruby-11.md

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
# 第11章: テスト文化入門 - Minitest
2+
3+
Rubyは動的型付け言語であり、コンパイル時ではなく実行時に型が決まります。これは柔軟で高速な開発を可能にする反面、型の不一致などによる単純なミスが実行時まで検出されにくいという特性も持ちます。
4+
5+
そのため、Rubyコミュニティでは「**テストは文化**」と言われるほど、自動化されたテストを書くことが重視されます。テストは、コードが期待通りに動作することを保証するだけでなく、未来の自分や他の開発者がコードをリファクタリング(修正・改善)する際の「安全網」として機能します。
6+
7+
この章では、Rubyに標準で添付されているテスティングフレームワーク「Minitest」を使い、テストの基本的な書き方と文化を学びます。
8+
9+
## 標準添付のテスティングフレームワーク「Minitest」
10+
11+
Minitestは、Rubyに標準で含まれている(=別途インストール不要)軽量かつ高速なテストフレームワークです。
12+
13+
Ruby on Railsなどの主要なフレームワークもデフォルトでMinitestを採用しており、Rubyのエコシステムで広く使われています。(RSpecという、よりDSL(ドメイン固有言語)ライクに記述できる人気のフレームワークもありますが、まずは標準のMinitestを理解することが基本となります。)
14+
15+
Minitestは、`Minitest::Test` を継承する「Unitスタイル」と、`describe` ブロックを使う「Specスタイル」の2種類の書き方を提供しますが、この章では最も基本的なUnitスタイルを学びます。
16+
17+
## テストファイルの作成と実行
18+
19+
早速、簡単なクラスをテストしてみましょう。
20+
21+
### 1\. テスト対象のクラスの作成
22+
23+
まず、テスト対象となる簡単な電卓クラスを作成します。
24+
25+
```ruby:calculator.rb
26+
# シンプルな電卓クラス
27+
class Calculator
28+
def add(a, b)
29+
a + b
30+
end
31+
32+
def subtract(a, b)
33+
a - b
34+
end
35+
end
36+
```
37+
38+
### 2\. テストファイルの作成
39+
40+
Rubyの規約では、テストファイルは `test_` プレフィックス(例: `test_calculator.rb`)または `_test.rb` サフィックス(例: `calculator_test.rb`)で作成するのが一般的です。ここでは `test_calculator.rb` を作成します。
41+
42+
テストファイルは、以下の要素で構成されます。
43+
44+
1. `require 'minitest/autorun'`
45+
* Minitestライブラリを読み込み、ファイル実行時にテストが自動で走るようにします。
46+
2. `require_relative 'ファイル名'`
47+
* テスト対象のファイル(今回は `calculator.rb`)を読み込みます。
48+
3. `class クラス名 < Minitest::Test`
49+
* テストクラスを定義し、`Minitest::Test` を継承します。
50+
4. `def test_メソッド名`
51+
* `test_` で始まるメソッドを定義します。これが個々のテストケースとなります。
52+
53+
```ruby:test_calculator.rb
54+
require 'minitest/autorun'
55+
require_relative 'calculator'
56+
57+
class CalculatorTest < Minitest::Test
58+
# `test_` で始まるメソッドがテストとして実行される
59+
def test_addition
60+
# テスト対象のインスタンスを作成
61+
calc = Calculator.new
62+
63+
# 期待値 (Expected)
64+
expected = 5
65+
# 実際の結果 (Actual)
66+
actual = calc.add(2, 3)
67+
68+
# アサーション(後述)
69+
# 期待値と実際の結果が等しいことを検証する
70+
assert_equal(expected, actual)
71+
end
72+
73+
def test_subtraction
74+
calc = Calculator.new
75+
# アサーションは1行で書くことが多い
76+
assert_equal(1, calc.subtract(4, 3))
77+
end
78+
end
79+
```
80+
81+
### 3\. テストの実行
82+
83+
ターミナルで、作成した**テストファイル**を実行します。
84+
85+
```ruby-exec:test_calculator.rb
86+
Run options: --seed 51740
87+
88+
# Running:
89+
90+
..
91+
92+
Finished in 0.001099s, 1819.8362 runs/s, 1819.8362 assertions/s.
93+
94+
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
95+
```
96+
97+
実行結果のサマリに注目してください。
98+
99+
* `.`(ドット): テストが成功(Pass)したことを示します。
100+
* `2 runs, 2 assertions`: 2つのテスト(`test_addition``test_subtraction`)が実行され、合計2回のアサーション(`assert_equal`)が成功したことを意味します。
101+
* `0 failures, 0 errors`: 失敗もエラーもありません。
102+
103+
もしテストが失敗すると、`F`(Failure)や `E`(Error)が表示され、詳細なレポートが出力されます。
104+
105+
## アサーション(assert\_equal, assert 等)の書き方
106+
107+
アサーション(Assertion = 表明、断言)は、「この値はこうあるべきだ」と検証するためのメソッドです。Minitestは `Minitest::Test` を継承したクラス内で、様々なアサーションメソッドを提供します。
108+
109+
### `assert_equal(expected, actual)`
110+
111+
最もよく使うアサーションです。「期待値(expected)」と「実際の結果(actual)」が `==` で等しいことを検証します。
112+
113+
> **⚠️ 注意:** 引数の順序が重要です。\*\*1番目が「期待値」、2番目が「実際の結果」\*\*です。逆にすると、失敗時のメッセージが非常に分かりにくくなります。
114+
115+
```ruby-repl
116+
irb> require 'minitest/assertions'
117+
=> true
118+
irb> include Minitest::Assertions
119+
=> Object
120+
irb> def assert_equal(expected, actual); super; end # irbで使うための設定
121+
=> :assert_equal
122+
123+
irb> assert_equal 5, 2 + 3
124+
=> true
125+
126+
irb> assert_equal 10, 2 + 3
127+
# Minitest::Assertion: <--- 失敗(Assertion Failed)
128+
# Expected: 10
129+
# Actual: 5
130+
```
131+
132+
### `assert(test)`
133+
134+
`test`**true**(またはtrueと評価される値)であることを検証します。偽(`false` または `nil`)の場合は失敗します。
135+
136+
```ruby-repl
137+
irb> assert "hello".include?("e")
138+
=> true
139+
irb> assert [1, 2, 3].empty?
140+
# Minitest::Assertion: Expected [] to be empty?.
141+
```
142+
143+
### `refute(test)`
144+
145+
`assert` の逆です。`test`**false** または `nil` であることを検証します。
146+
147+
```ruby-repl
148+
irb> refute [1, 2, 3].empty?
149+
=> true
150+
irb> refute "hello".include?("e")
151+
# Minitest::Assertion: Expected "hello".include?("e") to be falsy.
152+
```
153+
154+
### `assert_nil(obj)`
155+
156+
`obj``nil` であることを検証します。
157+
158+
```ruby-repl
159+
irb> a = nil
160+
=> nil
161+
irb> assert_nil a
162+
=> true
163+
```
164+
165+
### `assert_raises(Exception) { ... }`
166+
167+
ブロック `{ ... }` を実行した結果、指定した例外(`Exception`)が発生することを検証します。
168+
169+
これは、意図したエラー処理が正しく動作するかをテストするのに非常に重要です。
170+
171+
```ruby:test_calculator_errors.rb
172+
require 'minitest/autorun'
173+
174+
class Calculator
175+
def divide(a, b)
176+
raise ZeroDivisionError, "Cannot divide by zero" if b == 0
177+
a / b
178+
end
179+
end
180+
181+
class CalculatorErrorTest < Minitest::Test
182+
def test_division_by_zero
183+
calc = Calculator.new
184+
185+
# ブロック内で ZeroDivisionError が発生することを期待する
186+
assert_raises(ZeroDivisionError) do
187+
calc.divide(10, 0)
188+
end
189+
end
190+
end
191+
```
192+
193+
```ruby-exec:test_calculator_errors.rb
194+
Run options: --seed 19800
195+
196+
# Running:
197+
198+
.
199+
200+
Finished in 0.000624s, 1602.5641 runs/s, 1602.5641 assertions/s.
201+
202+
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
203+
```
204+
205+
## 簡単なTDD(テスト駆動開発)の体験
206+
207+
TDD (Test-Driven Development) は、機能のコードを書く前に、**まず失敗するテストコードを書く**開発手法です。TDDは以下の短いサイクルを繰り返します。
208+
209+
1. **Red (レッド):**
210+
* これから実装したい機能に対する「失敗するテスト」を書きます。
211+
* まだ機能が存在しないため、テストは(当然)失敗(Red)します。
212+
2. **Green (グリーン):**
213+
* そのテストをパスさせるための**最小限の**機能コードを実装します。
214+
* テストが成功(Green)すればOKです。コードの綺麗さはまだ問いません。
215+
3. **Refactor (リファクタリング):**
216+
* テストが成功した状態を維持したまま、コードの重複をなくしたり、可読性を上げたりする「リファクタリング」を行います。
217+
218+
`Calculator` クラスに、`multiply`(掛け算)メソッドをTDDで追加してみましょう。
219+
220+
### 1\. Red: 失敗するテストを書く
221+
222+
まず、`test_calculator.rb``multiply` のテストを追加します。
223+
224+
```ruby:calculator.rb
225+
# シンプルな電卓クラス
226+
class Calculator
227+
def add(a, b)
228+
a + b
229+
end
230+
231+
def subtract(a, b)
232+
a - b
233+
end
234+
end
235+
```
236+
237+
```ruby:test_calculator_tdd.rb
238+
require 'minitest/autorun'
239+
require_relative 'calculator' # calculator.rb は add と subtract のみ
240+
241+
class CalculatorTest < Minitest::Test
242+
def setup
243+
# @calc をインスタンス変数にすると、各テストメソッドで使える
244+
@calc = Calculator.new
245+
end
246+
247+
def test_addition
248+
assert_equal(5, @calc.add(2, 3))
249+
end
250+
251+
def test_subtraction
252+
assert_equal(1, @calc.subtract(4, 3))
253+
end
254+
255+
# --- TDDサイクル スタート ---
256+
257+
# 1. Red: まずテストを書く
258+
def test_multiplication
259+
assert_equal(12, @calc.multiply(3, 4))
260+
end
261+
end
262+
```
263+
264+
この時点で `calculator.rb``multiply` メソッドは存在しません。テストを実行します。
265+
266+
```ruby-exec:test_calculator_tdd.rb
267+
# (実行結果の抜粋)
268+
...
269+
Error:
270+
CalculatorTest#test_multiplication:
271+
NoMethodError: undefined method `multiply' for #<Calculator:0x...>
272+
...
273+
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
274+
```
275+
276+
期待通り、`NoMethodError` でテストが**エラー (E)** になりました。これが「Red」の状態です。(Failure (F) はアサーションが期待と違った場合、Error (E) はコード実行中に例外が発生した場合を指します)
277+
278+
### 2\. Green: テストを通す最小限のコードを書く
279+
280+
次に、`calculator.rb` に以下のように `multiply` メソッドを実装し、テストをパス(Green)させます。
281+
282+
```ruby
283+
class Calculator
284+
def add(a, b)
285+
a + b
286+
end
287+
288+
def subtract(a, b)
289+
a - b
290+
end
291+
292+
# 2. Green: テストを通す最小限の実装
293+
def multiply(a, b)
294+
a * b
295+
end
296+
end
297+
```
298+
299+
`calculator.rb` を編集し、再びテストを実行すると、以下のようにすべてのテストが成功します。「Green」の状態です。
300+
301+
```bash
302+
$ ruby test_calculator_tdd.rb
303+
...
304+
Finished in ...
305+
3 runs, 3 assertions, 0 failures, 0 errors, 0 skips
306+
```
307+
308+
### 3\. Refactor: リファクタリング
309+
310+
今回は実装が非常にシンプルなのでリファクタリングの必要はあまりありませんが、もし `multiply` の実装が複雑になったり、他のメソッドとコードが重複したりした場合は、この「Green」の(テストが成功している)状態で安心してコードをクリーンアップします。
311+
312+
TDDは、この「Red -\> Green -\> Refactor」のサイクルを高速で回すことにより、バグの少ない、メンテンスしやすいコードを堅実に構築していく手法です。
313+
314+
## 📈 この章のまとめ
315+
316+
* Rubyは動的型付け言語であるため、実行時の動作を保証する**テストが非常に重要**です。
317+
* **Minitest**Rubyに標準添付された軽量なテスティングフレームワークです。
318+
* テストファイルは `require 'minitest/autorun'` し、`Minitest::Test` を継承します。
319+
* テストメソッドは `test_` プレフィックスで定義します。
320+
* `assert_equal(期待値, 実際の結果)` が最も基本的なアサーションです。
321+
* `assert` (true検証), `refute` (false検証), `assert_raises` (例外検証) などもよく使われます。
322+
* **TDD (テスト駆動開発)** は「Red (失敗) -\> Green (成功) -\> Refactor (改善)」のサイクルで開発を進める手法です。
323+
324+
### 練習問題1: Stringクラスのテスト
325+
326+
`Minitest::Test` を使って、Rubyの組み込みクラスである `String` の動作をテストする `test_string.rb` を作成してください。以下の2つのテストメソッドを実装してください。
327+
328+
* `test_string_length`: `"hello"``length``5` であることを `assert_equal` で検証してください。
329+
* `test_string_uppercase`: `"world"``upcase` した結果が `"WORLD"` であることを `assert_equal` で検証してください。
330+
331+
```ruby:test_string.rb
332+
require 'minitest/autorun'
333+
334+
335+
```
336+
337+
```ruby-exec:test_string.rb
338+
```
339+
340+
### 練習問題2: TDDでUserクラスを実装
341+
342+
TDDの「Red -\> Green」サイクルを体験してください。
343+
344+
1. (Red`User` クラスに `first_name``last_name` を渡してインスタンス化し、`full_name` メソッドを呼ぶと `"First Last"` のように連結された文字列が返ることを期待するテスト `test_full_name` を含む `test_user.rb` を先に作成してください。(この時点では `user.rb` は空か、存在しなくても構いません)
345+
2. (Green)テストがパスするように、`user.rb``User` クラスを実装してください。(`initialize` で名前を受け取り、`full_name` メソッドで連結します)
346+
347+
348+
```ruby:user.rb
349+
```
350+
351+
```ruby:test_user.rb
352+
require 'minitest/autorun'
353+
require_relative 'user'
354+
355+
```
356+
357+
```ruby-exec:test_user.rb
358+
```

0 commit comments

Comments
 (0)