Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Arrange・Act・Assertの三相パターンで、より良いテストを書こう

(これは『Practicing Rails』からの短い抜粋です。こちらから登録すると、第1章を無料で入手できます!)

新しいアプリを開発していて、Railsが自動的にテストファイルを生成してくれたとしましょう。

test/models/bug_test.rb
require 'test_helper'

class BugTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
end

コメントアウトを外して、テスト名を決めれば、あとはテストコードを書くだけ。でも、いったい何から書けばいいのでしょうか? 最初に何を書き、テストコードはどんな構造にすべきなのでしょう?

シンプルなパターンに従うだけで、雛形のコードは明快で構造化されたテストケースへと変わります。

三相(スリーフェーズ)テストパターンとは

テストケースは、次の3つのフェーズで構成するのが基本です。

  1. まず、必要なデータや状態を準備する(Arrange:準備
  2. 次に、テスト対象の処理を実行する(Act:実行
  3. 最後に、期待した結果が実際に得られたかを確認する(Assert:検証

たとえば、Rubyの配列メソッドをテストする場合、このパターンに沿うとテストは次のようになります。

test "Array#sort will sort an array of numbers" do
  # arrange(準備)
  unsorted_array = [7, 4, 2, 3]
  
  # act(実行)
  sorted_array = unsorted_array.sort

  # assert(検証)
  assert_equal [2, 3, 4, 7], sorted_array
end

とてもシンプルですね。しかし重要なのは、テストの各部分にはそれぞれ「置き場所」があり、各フェーズがそのまま「どう書けばいいか」を教えてくれるという点です。

場合によってはArrangeフェーズが不要だったり、ActとAssertが一体化したりすることもあります。それでも、3つのフェーズを意識しながらテストを書くことは、大きな助けになります。

Assertフェーズで陥りやすい落とし穴

Assertフェーズには一つ注意点があります。Actフェーズで使ったのと同じロジックを、Assertフェーズでもう一度使ってはいけないということです。同じ答えに到達するための「2つの異なる経路」を必ず用意しましょう。そうしないと、呼び出しているコード自体のバグに気づけなくなります。Assertフェーズで同じコードが再度呼ばれるだけになってしまうからです。

たとえば、数値の平均を計算するメソッドをテストする場合を見てみましょう。

test "average returns the average of a set of numbers" do
  # arrange(準備)
  numbers = [1, 2, 3, 4]
  
  # act(実行)
  average = numbers.average

  # assert(検証)
  
  # これは悪い例
  assert_equal [1, 2, 3, 4].average, average

  # これは良い例
  assert_equal 2.5, average
end

Assertフェーズで[1, 2, 3, 4].averageをもう一度呼び出すのは良くありません。averageがほぼどんな値を返しても、このアサーションは通ってしまうからです。

この例では問題が明白ですが、テストが複雑になっても同じ原則が適用されます。同じコードを二度実行しているだけなら、それはメソッドが呼び出されたことしか確認しておらず、期待どおりに動作していることまでは保証できません。

通常、答えへの2つ目の経路を作る最も簡単な方法は、答えを手作業で求めてハードコードすることです。少し脆くなる面はありますが、壊れていることに気づかないままテストを運用し続けるより、ずっとマシです。

なぜ3つのフェーズに分けるのか

テストを3つのフェーズに分割すると、答えるべき問いがシンプルになります。「このテスト全体をどう書けばいいのか?」という漠然とした悩みの代わりに、「準備はどうすればいいか?」「何をテストしているのか?」「正解はどんな形になるべきか?」と、各フェーズごとに集中できるのです。

これらの問いも簡単に答えられるとは限りませんが、テスト全体を一度に考えるより、はるかに楽に答えられるはずです。さらに運が良ければ、関連するテスト間でフェーズを共有できることもあり、次のテストを書く負担がぐっと軽くなります。

  1. Swift 3でアプリを作成する方法|新機能・開発環境・学習リソースを徹底解説

    Swiftは、macOSやiOSデバイス向けのアプリを開発するために使われるプログラミング言語です。Appleはデバイスから最速かつ最高効率のパフォーマンスを引き出すことを目的としてSwiftを設計しており、Swift 3では既に高い評価を受けていた機能セットがさらに拡張されました。 Swift 3の誕生背景:オープンソースコミュニティが生んだ初のメジャーアップデート 「Swiftはわずか6ヶ月前にオープンソースプロジェクトとして公開されました」と、Appleで言語およびランタイム部門のシニアマネージャーを務めるTed Kremenek氏は語ります。「GitHubにソースコードを公開すること

  2. 【Ruby】デコレータデザインパターンでクラスを変更せずにオブジェクトを拡張する方法

    デコレータ(Decorator)デザインパターンとは、どのようなものなのでしょうか? また、このパターンをRubyのプロジェクトではどのように活用すればよいのでしょうか? デコレータデザインパターンを利用すると、クラスそのものを変更することなく、オブジェクトに新しい機能を追加して拡張することができます。 それでは、具体的な例を見ていきましょう! ログ出力とパフォーマンス計測の実装例 この例では、rest-clientのようなGemを使ってHTTPリクエストを送信します。 コードは次のようになります。 require restclient data = RestClient.get(www.