Help us understand the problem. What is going on with this article?

Rspecでのユニットテスト

100度超えサウナでないと満足できない、プレイライフのサウナエンジニアの合原です。

当社では、現在、Ruby on Railsを用いてサービス開発をしています。また、Rspecを使ったテストも普段から行なっています。

そこで、今回はテストの効用、ユニットテストの重要性and少しだけサンプルを示してみます。

対象読者

  • テストの効用がよくわからない人
  • ユニットテストを書いたことがない人
  • ユニットテストの効果がよくわからない人
  • 効果的なユニットテストを書くにはどうすればよいか学びたい人
  • プレイライフではエンジニアリングをしているのか興味がある方

Rspecとは

こちらは言わずと知れた、ポピュラーなテストフレームワーク。
構文には一種のくせ(?)あるため、敬遠する人もいるが、一度覚えると、柔軟なテストができます。

Rspecのメリット

  • Rubyで最もポピュラーなテスティングフレームワーク
  • メタ視点でのテストが書けるため、テストコードが自然言語っぽくなる
  • ユニットテストに限らず、結合テストやブラウザテストなどにも使える

Rspecデメリット

  • 一定の学習コストがある

反面、一度覚えれば、より柔軟なテストが可能となる。

なぜテストを書くのか

大きくは、以下の3つ。

  • 改修時など、テストが失敗することで漏れや変更点が明確になり、デグレを防げる
  • テスト自体が仕様となるため、コードの理解がしやすくなる
  • コードの品質を上げられる

ユニット(単体)テストとは

ユニットテストとは、ソースコードの個々のユニット、すなわち、1つ以上のコンピュータプログラムモジュールが使用に適しているかどうかを決定するために、関連する制御データ、使用手順、操作手順とともにテストする手法である[1]。ユニットとはアプリケーションのテスト可能な最小の部品単位である、と直観的にとらえることができる

言い換えると、メソッドやクラスと言った単位で、想定された仕様どおりに動作しているかを検証するテスト。

なぜユニットテストが重要なのか

基本的に、アプリケーションは各種モジュールの束でできているため、各モジュール単位に分解して、それぞれをの動作を検証(テスト)することは不可欠になります。
これができていると、さらに上位の各機能の結合テストはさらに容易になる。
自ずと、アプリケーションのテスト網羅性は高めることができます。

基本的な考え方

ユニットテストでは、クラスやメソッドを最小処理単位として扱い、引数を処理した「結果」と、想定される「期待値」(これを振る舞いと言う)の2つを検証(比較)します。

ここで、以下のような引く数を受け取り1を加算するだけのメソッドをテストすることを考えてみます。

class Calc
  def add(arg)
     arg + 1
  end
end

1.テストケースを想定する

上記のメソッドからテストケースを想定すると、大きくは以下の2つのケースが考えられます。

  1. 引数がnilの場合
  2. 引数が正常な数値の場合
  3. 引数が文字列等の場合

rspecで書くと..

RSpec.describe Calc, type: :model do # テスト対象となるクラス
  describe '#add' do #テスト対象となるメソッド
    context '引数がnilの場合' do
      [ここに具体的なテスト内容]
    end
    context '引数が正常な数値の場合' do
      [ここに具体的なテスト内容]
    end
    context '引数が不正な値(文字列等)の場合' do
      [ここに具体的なテスト内容]
    end
  end
end

※ contextの箇所は英語でも日本語でもわかればいいと思います。テストの本質とは全く関係ないため。

こうすることで

  • メソッドの振る舞いが明確になる
  • テストを見るだけで、このメソッドの動作がわかる
  • 仮にレビュアーであれば、ここを見るだけで、実は他にも動作ケースがありうるのであればテストケース漏れを指摘しやすくもなる
  • 仮にプロダクション環境にデプロイ後でも、ここを見れば漏れが明確になり、修正が容易になる(メンテコスト下がる)
  • 既存の振る舞いが明確なので、リファクタリングもしやすい

2. 実際のテストコード

最終的なコードは下記の通り。
※細かいrspecの文法については割愛。

RSpec.describe Calc, type: :model do # テスト対象となるクラス
  describe '#add' do #テスト対象となるメソッド
    subject { Calc.new.add(arg)}
    context '引数がnilの場合' do
      expect { subject }.to raise_error NoMethodError
    end
    context '引数が正常な数値の場合' do
      let(:arg) { 1 }
      it { is_expected.to eq 2 }
    end
    context '引数が不正な値(文字列等)の場合' do
      let(:arg) { 'a' }
      expect { subject }.to raise_error TypeError
    end
  end
end

まとめ

と言うわけで、あくまで簡易な例で一気に説明しましたが、ユニットテストを行うことで、
* コードの品質向上
* 将来的なメンテコストの下げられる(場合もある)

といったことが望めるのは少しおわかりいただけたか(?)と思います。
もちろん、Rspecに慣れないといけないと言った面もあります。
これからしっかりテストコードを書いていきたいと言う方に少しでも、お役に立てたら幸いです。

また、当社では、テストを軽視することなく、何よりもスピーディなサービス開発を実現することにも、果敢に挑戦しています。気になる方は、ぜひ、こちらから。

あわせて読みたい

RSpecの公式ドキュメント
オブジェクト指向設計実践ガイド

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした