📌

単体テストとか結合テストって結局なんなの?

に公開

Daily codingならぬDaily blogging2日目

二日目にしていきなり体調不良になり、今日はほとんどインプットできてません😇
とりあえず読みかけの単体テストの考え方/使い方で知ったことをアウトプットしてみる

単体テストって結局なんなんでしょうか

この本を読むまでは、なんとな〜く「単体テスト」っていうのは、一つのクラスとかモジュールとかメソッドに対するテストのことを指すのかなぁと思ってた。
「結合テスト」は、複数のクラスとかを組み合わせたテスト
例えばコントローラーからサービスクラスとか呼び出して〜最終的なコントローラから返るレスポンスは?みたいなやつ。

一つの振る舞いを定義したものが「単体テスト」

クラスとかそんなん関係なくて、「一つの振る舞いを定義したもの」が単体テストらしい
この考え方を古典学派っていうみたい。
逆に私みたいな考え方はロンドン学派。
ちなみにt_wadaさんとかは古典学派

二つの違い

by chatgpt

特徴 古典学派 (Classicist) ロンドン学派 (Londonist)
テスト対象 実装全体の動作をテスト インターフェースやユニット間の連携をテスト
依存関係 実際の依存関係を使用 モックやスタブを積極的に使用
リファクタ 実装変更に強い モックの変更が必要になることがある
速度 依存関係が多いと遅くなる 依存関係を分離しているため高速
メリット 実際の動作に近いテストができる ユニット間のインタラクションを細かくテスト可能
デメリット 複雑な依存関係ではテストが煩雑になる 実装に依存するためリファクタ時に手間が増える

テストの例

by chatgpt
こういうサービスクラスあたとして

# user_service.rb
class UserService
  def initialize(user_repository)
    @user_repository = user_repository
  end

  def find_user_name_by_id(user_id)
    user = @user_repository.find_by_id(user_id)
    user ? user[:name] : nil
  end
end

# user_repository.rb
class UserRepository
  def initialize
    @users = { 1 => { id: 1, name: 'Alice' } }
  end

  def find_by_id(user_id)
    @users[user_id]
  end
end

古典学派だとこう

# user_service_test.rb
require 'minitest/autorun'
require_relative 'user_service'
require_relative 'user_repository'

class UserServiceTest < Minitest::Test
  def setup
    @repository = UserRepository.new
    @service = UserService.new(@repository)
  end

  def test_find_user_name_by_id_returns_name
    assert_equal 'Alice', @service.find_user_name_by_id(1)
  end

  def test_find_user_name_by_id_returns_nil_if_not_found
    assert_nil @service.find_user_name_by_id(99)
  end
end

ロンドン学派だとこう

# user_service_test.rb
require 'minitest/autorun'
require_relative 'user_service'

class UserServiceTest < Minitest::Test
  def setup
    @mock_repository = Minitest::Mock.new
    @service = UserService.new(@mock_repository)
  end

  def test_find_user_name_by_id_returns_name
    @mock_repository.expect(:find_by_id, { id: 1, name: 'Alice' }, [1])

    assert_equal 'Alice', @service.find_user_name_by_id(1)

    @mock_repository.verify
  end

  def test_find_user_name_by_id_returns_nil_if_not_found
    @mock_repository.expect(:find_by_id, nil, [99])

    assert_nil @service.find_user_name_by_id(99)

    @mock_repository.verify
  end
end

Discussion