Webとデザインのあれこれ

プログラミングとUIデザインの学習記録です。

自動化テストの基本

自動テストについて

前回の記事まで、gemを使ってBookアプリに様々な機能を実装しました。本記事より自動化テストを扱います。

まずは、その中でも代表的な手法であるTDD(テスト駆動開発)について学習しましょう!

TDDとテストの定義

TDDとはTest Driven Developmentの略で、日本語では「テスト駆動開発」と呼ばれています。Kent Beck氏によって提唱された概念です。

こちらの書籍も非常に有名ですよね。

テスト駆動開発(以下、TDDと略)の「テスト」ってそもそも何をテストするのでしょうね。テストの観点は大きく分けると以下の2種類に分けられます。

  1. 機能要件
  2. 非機能要件

上記の内、機能要件を扱うのがTDDにおけるテストのことでDeveloper Testingと呼びます。文字通り、開発者によるテストのことを指します。

一方、堅牢性や情報セキュリティといった仕様や機能以外の非機能要件をテストするのは品質保証などを扱うQAテストです。一般的にテストというと、こちらを連想するかもしれませんね。

TDDとは、機能の期待通りの動作を担保するために、まずテストコードを書いて開発を進める手法を指します。テストファーストという概念自体が個人的にはすごく新鮮です。

TDDのサイクル

TDDは以下の3プロセスから構成されています。

  1. Red
  2. Green
  3. Refactor

それぞれを確認しましょう。

1. Red

機能要件を元に、メソッド内容IFと出力結果を記載した失敗するテストを書きます。

2. Green

失敗するテストを元に実装を進め、テストを成功させる最低限のコードを書きます。

3. Refactor

完成したテストが失敗しないようにコードの内容を変更、必要に応じてテストを追加します。

コードの変更がある場合は再度失敗するテストを書き、テストに合わせてコードを実装しサイクルを回していきます。

上記の3ステップを素早く小さく繰り返すことできれいな動くコードを目指します。

テストの種類

Developer Testingもその目的によって何種類かに分かれます。

単体テストとは、1クラスや1メソッドを対象とした最小単位のテストのことで、機能が意図した通りに正しく動作するかどうかを検証します。 テストの粒度が細かく、バグが発生した箇所を細かく分析することができるなどのメリットがあります。

より対象を広げ,複数のメソッドやモジュール間の連携をテストするのが機能テストです。こちらはテストの粒度が大きめです。

一般的にバグ発見などのメリットがあることや粒度が大きいテストはメンテナンスが煩雑になることなどから、単体テスト(ユニットテスト)を優先して行う傾向があるそうです。

先程取り上げたTDDは、ユニットテストリファクタリングを両輪とした非常に小さくて狭いフィードバックサイクルを回す手法と言えますね。

Rubyにおけるテスティングフレームワーク

Railsを扱うため、ここからはRuby標準のテスティングフレームワークについて確認しましょう。テストにおけるフレームワークは以下の3種類です。

  1. Minitest
  2. RSpec
  3. test-unit

RSpec以外はRubyのインストール時に同梱されるのでgemのインストールは不要です。

また、Minitestとtest-unitについてはテストコードも似ており、RSpecは独自のDSLを用いるため記法が異なっているという特徴があります。

何故3種類も存在しているかというと、Rubyのバージョン毎に標準のフレームワークが変わっていたという複雑な(?)歴史があったためです。

Minitestとtest-unitについては現在両方とも標準として使用可能ですので、両方理解できているといいですね。

今回はこの中でもtest-unitに焦点を当てます!

test-unitについて

RubyにはTEST::Unitというクラスライブラリが存在し、こちらを用いて単体テスト(ユニットテスト)を行います。

検証メソッドはいたってシンプル。数種類存在しますが、assert_equal b(期待値), a(実際の値)(aがbと等しければパスする)などが使えれば問題なさそうです。

では、コードを実際に見てみましょう!

以下は、『プロを目指す人のためのRuby入門』第3章の内容を参考にしたプログラムです。

# テストコード
require 'test/unit'
require './lib/fizz_buzz'

class FizzBuzzTest < Test::Unit::TestCase
  def test_fizz_buzz
    assert_equal '1', fizz_buzz(1)
    assert_equal '2', fizz_buzz(2)
    assert_equal 'Fizz', fizz_buzz(3)
  end
end

まず、Test::Unit::TestCaseを継承したクラス(この場合FizzBuzzTest)を用意します。

そのクラス内でtest_で始まるメソッド(この場合はtest_fizz_buzz)を定義するとそのメソッドがテストの実施対象になります。

もちろんテストコードとは別に、ファイルを分けてfizz_buzzというメソッドを記述しています。

# fizz_buzz.rb
def fizz_buzz(n)
  if n % 21  == 0
    'Fizz Buzz'
  elsif n % 3 == 0
    'Fizz'
  elsif n % 7 == 0
    'Buzz'
  else
    n.to_s
  end
end

実行方法は、Ruby ファイル名でOKです。比較的簡単にテストが実施できますね。

実行結果の画面

f:id:b_leiu:20190523024034p:plain

最後に

いかがでしたでしょうか。test-unitは素のRubyで比較的簡単にテストが実施できるので便利ですね。 次回よりRSpecを中心に記事を書く予定です。

参考文献:

プロを目指す人のためのRuby入門

qiita.com

e-words.jp

webos-goodies.jp

qiita.com

gihyo.jp

codezine.jp

www.slideshare.net