RSpecからpytestへ:戸惑いがちな書き方の対応チートシート
副業のプロダクト開発でpytestを使ったユニットテストを書き始めたとき、それまでRSpecしか経験がなく、最初はかなり戸惑いました。「RSpecのbefore句はpytestで何を使えばいい?」「factorybotの代わりは?」という具体的な疑問が次々と出てきました。
同じようにRSpec経験からpytestへ移行する場面を想定して、よく使う書き方の対応をまとめます。
この記事は RSpec 3.x と pytest 7.x〜8.x を前提にしています。RSpec公式とpytest公式はそれぞれ下記から参照してください。

RSpec: Behaviour Driven Development for Ruby
pytest documentation
逆引き対応表
| やりたいこと | RSpec の書き方 | pytest の書き方 |
|---|---|---|
| テスト前の準備処理 | before do ... end | @pytest.fixture |
| 特定テストだけ実行 | rspec spec/foo_spec.rb:12 | pytest -k "テスト名の一部" |
| ダミーデータ生成 | FactoryBot | factory_boy |
| デバッガの起動 | binding.pry | breakpoint() |
| 同じテストを複数の入力で回す | shared_examples | pytest.mark.parametrize |
テスト実行:-k オプションで実行対象を絞る
RSpecでは実行ファイルと行番号を指定してテストを絞れます。
$ rspec spec/sample_spec.rb:12
pytestで同じことをするには -k オプションを使います。渡した文字列に 部分一致 するテストが実行されます。
$ pytest -k sample_test
sample_test を含む sample_test_1 や sample_test_2 もまとめて実行されます。完全一致ではない点に注意です。
テスト前の準備:before の代わりに fixture を使う
RSpecでは before 句でテスト前の処理を書きます。
before do
# テスト前に実行したい処理
end
relishapp.com/rspec/rspec-core/v/2-0/docs/hooks/before-an…
pytestでは @pytest.fixture デコレータを使って同じことを実現します。フィクスチャ関数をテスト関数の引数として受け取るだけで自動的に実行されます。
import pytest
@pytest.fixture
def user_data():
return {"name": "test_user", "email": "test@example.com"}
def test_user_name(user_data):
assert user_data["name"] == "test_user"
How to use fixtures - pytest documentation
ダミーデータ:FactoryBot の代わりに factory_boy を使う
RSpecでは FactoryBot でダミーデータを定義します。
FactoryBot.define do
factory :user do
name { 'name' }
end
end
GitHub - thoughtbot/factory_bot_rails: Factory Bot ♥ Rails
Factory Bot ♥ Rails. Contribute to thoughtbot/factory_bot_rails development by creating an account on GitHub.
pytestでは factory_boy で同じことができます。クラス定義の構文がやや違うだけで、発想はほぼ同じです。
import factory
from app.models import User
class UserFactory(factory.Factory):
class Meta:
model = User
name = 'name'
factory_boy — Factory Boy stable documentation
デバッガ:binding.pry の代わりに breakpoint() を使う
Railsでのデバッグでは binding.pry が定番です。
require 'pry'; binding.pry
pytestでは Python 3.7 以降で使える組み込みの breakpoint() が現在の標準です。以前は pdb.set_trace() が使われていましたが、より短く書けます。
def test_something():
result = do_something()
breakpoint() # ここでデバッガが起動する
assert result == expected
また、--pdb オプションをつけてテスト実行すると、失敗したタイミングで自動的にデバッガが起動します。
$ pytest --pdb sample_test.py
複数パターンのテスト:shared_examples の代わりに parametrize を使う
RSpecでは shared_examples を使って同じテストを複数の入力値で実行できます。
pytestには pytest.mark.parametrize デコレータがあります。
import pytest
@pytest.mark.parametrize("input_val, expected", [
(1, 2),
(2, 4),
(3, 6),
])
def test_double(input_val, expected):
assert input_val * 2 == expected
入力と期待値のペアをリストで渡すだけで、全パターンが独立したテストとして実行されます。テスト名も自動的に test_double[1-2] のように区別されるので、失敗したときにどのパターンか一目でわかります。
移行直後は「RSpecなら一行で書けたのに」と感じる場面もありましたが、pytestはpytestで書き方が整理されていて、慣れると使いやすいフレームワークです。対応表を手元に置いておくと、最初の数日の戸惑いをかなり短縮できると思います。
記事の更新をメールで受け取る
質問・リクエストを送る
記事についての質問や、取り上げてほしいテーマがあればお気軽にどうぞ。いただいた質問はブログ記事として回答し、Q&Aページで公開することがあります。