【RSpec】 管理者・一般ユーザーを分けてテストデータを作成する
実装したいこと
管理者と一般ユーザーで権限が違うため、それぞれのテストデータを作成したい。
trait
トレイトを使う。 属性を予めセットして定義できる機能。
trait:A distinguishing quality or characteristic, typically one belonging to a person.
FactoryBot.define do factory :user do sequence(:name) { |n| "general-#{n}" } #sequenceで一意のデータを生成する。 password { 'password' } password_confirmation { 'password' } role { :general } #default値を設定する。 trait :admin do sequence(:name) { |n| "admin-#{n}" } role{ :admin } end trait :general do sequence(:name) { |n| "general-#{n}" } role{ :general } end end end
trait
の使い方がいまいち理解できてなかったのですが、
デフォルトでは全ての属性をセットしておく。→ trait
は指定したい属性だけ切り取ってセットする。
っていうことですね。
マクロを使ってログイン機能だけセットする
module LoginMacro def login_as(user) visit login_path fill_in 'user[name]', with: user.name click_button '次へ' fill_in 'user[password]', with: 'password' #user.passwordはnil click_button 'ログイン' end end
※ rails_helper.rb
で読み込み設定をしておきます。
(略) Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } #support以下を読み込む RSpec.configure do |config| (略) config.include FactoryBot::Syntax::Methods #FacctoryBotの記述を省略できる config.include LoginMacros #LoginMacrosを読み込む end
module ? macros?
以前もログイン機能の切り出しはしたのですが、そのときはLoginModule
としていました。
Macro
Module
どっち?…と疑問に思ったので講師の方に質問したところ、慣例で現場によって異なるのでどっちでもいいとのことでした。
System Spec
require 'rails_helper' RSpec.describe "AdiminArticlesPreviews", type: :system do let(:admin) { create :user, :admin } describe '記事作成画面で画像ブロックを追加' do context '画像なしで投稿する' do it 'プレビューが正常に表示される' do login_as(admin) (略) switch_to_window(windows.last) #windowを切り替え、最後のwindowを指定 expect(page).to have_content('test') end end end end
let(:admin) { create :user, :admin }
let(定義名) { 内容 }
です。
{ create :user, :admin }
の:admin
部分で、さっき定義したtrait
を使っています。
login_as(admin)
マクロで定義したログイン用のメソッドを使っています。
引数に、let
で作成したadmin
を渡して、管理者としてログインしています。
System Specの命名について
Controllerが、admin/articles/previews_controller
のように名前空間で分かれているとき、どうやってファイル名をつけたらいいのかな?って思ったけど、普通に、admin_articles_previews_spec.rb
とするみたい。
NGコード
最初に私が実装したコードを反省用に残しておく。一応動くけど酷い、汚い。。
describe
,context
とbefore
をなんとなくで使っているので勉強します
require 'rails_helper' RSpec.describe "Previews", type: :system do let(:admin) { create(:user, :admin) } before{ login_as(admin) } describe '記事の画像投稿機能' do before do click_link '記事' click_link '新規作成' fill_in 'タイトル', with: 'test' click_button '登録する' click_link 'ブロックを追加する' click_link '画像' end context '画像なしで投稿する' do it 'プレビューが見れること' do click_link 'プレビュー' switch_to_window(windows.last) expect(page).to have_content('test') end end end end
参考
トレイトについて 『everydayRails』P64
【RSpec】gem Seed Fu
gem Seed Fu
Seed Fu is an attempt to once and for all solve the problem of inserting and maintaining seed data in a database. It uses a variety of techniques gathered from various places around the web and combines them to create what is hopefully the most robust seed data system around.
GitHub - mbleigh/seed-fu: Advanced seed data handling for Rails, combining the best practices of several methods together.
シードを挿入してくれるgem
テスト環境にシードデータを投入するのに、以下のコマンドを実行していました。
rails db:seed_fu RAILS_ENV=test
こっちでもいい
RSpec.configure do |config| (略) config.before :suite do SeedFu.seed end end
before(:suite)
はテスト実行前に実行される。
(『everydayRails』p42,171)
SeedFu.seed
ってどういう意味??と思ったら、公式READ.MEの中に、以下の一文があった。
You can also do a similar thing in your code by calling SeedFu.seed(fixture_paths, filter).
参考
railsで初期データを入れる(seed-fuの使い方) - Qiita
SeedFu.seedのコードリーディングはこちらを参考にした。
seed-fuコードリーディング
【Rails】雑多なメモ
知らないメソッドが色々ありすぎて、忘れそうなのでメモしていきます。
protect_from_forgery with: :exception
# Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception
protect_from_forgery | Railsドキュメント
protect_from_forgery with: :exception
このコードがあると、Railsで生成されるすべてのフォームとAjaxリクエストにセキュリティトークンが自動的に含まれます。セキュリティトークンがマッチしない場合には例外がスローされます。クロスサイトリクエストフォージェリ (CSRF)
この攻撃方法は、ユーザーによる認証が完了したと考えられるWebアプリケーションのページに、悪意のあるコードやリンクを仕込むというものです。そのWebアプリケーションへのセッションがタイムアウトしていなければ、攻撃者は本来認証されていないはずのコマンドを実行できてしまいます。
Rails セキュリティガイド - Railsガイド
helper_method
def current_site @current_site ||= Site.first end helper_method :current_site
helper_method (AbstractController::Helpers::ClassMethods) - APIdock
コントローラー内のメソッドをヘルパーとして宣言する。
viewでもこのメソッドが使えるようになる。
gem pundit
認可に使用する。
ユーザーによってできることを限定したり、許可したりするのに使う。
Pundit provides a set of helpers which guide you in leveraging regular Ruby classes and object oriented design patterns to build a simple, robust and scaleable authorization system.
File: README — Documentation for pundit (2.1.0)
class ApplicationController < ActionController::Base include Pundit (略) end
Pundit is focused around the notion of policy classes. We suggest that you put these classes in app/policies. This is a simple example that allows updating a post if the user is an admin, or if the post is unpublished:
class PostPolicy attr_reader :user, :post def initialize(user, post) @user = user @post = post end def update? user.admin? or not post.published? end end
slug
位置を示すコード
URLの末尾
ユーザーフレンドリーになるように、URLを文字列型にする。
以下のstackoverflowの回答が分りやすかった。
It's simply a way to create user-friendly URLs:
【Rails】ArgumentError - Nil location provided. Can't build URI エラーが出たら
エラーで悩んだのでメモ
画像を投稿するページで、画像を選択しないでプレビュー画面を開こうとすると以下のエラーになりました。
エラー画面
ArgumentError - Nil location provided. Can't build URI
ArgumentError - Nil location provided. Can't build URI.: app/views/shared/_media_image.html.slim:5:in `_app_views_shared__media_image_html_slim___195936332176276977_70365205560160' app/models/article.rb:76:in `block in build_body' app/models/article.rb:70:in `build_body' app/controllers/admin/articles/previews_controller.rb:9:in `show'
View修正
画像がなかった場合を想定していないコードだったので、エラーが出てしまいました。
改善方法は2つありますが、今回は①を選択しました。
① image_tag
があるときだけrender
される
②image_url
にデフォルト値を設定しておく
#① - if medium.image_url #追加 .media-image = image_tag medium.image_url(:lg) #② = image_tag medium.image_url || default_image
※最初、if
を中に入れ込んでしまったけど、これだと、elseのときにmedia-imageに空タグが入ってしまうのでNG
.media-image - if medium.image_url = image_tag medium.image_url(:lg)
参考
ArgumentError Nil location provided. Can't build URI. が出た時の対処法 プロフ画像の実装 | びんぼーろく
【RSpec】System Spec
- 今回の記事の目的
- 実行するテストケースを限定したいとき
- 別タブで開いたページをテストしたいとき
- 確認画面のページ操作したいとき
- ApplicationHelperで定義したメソッドをRSpecで使いたいとき
- Updateのテストがうまくいかない。値が更新されないとき
- FactoryBotのtraitを使う
- Capybaraのメソッドについて
今回の記事の目的
System Specのコードを書いていて迷うことがいくつかあったので、復習していきます。
前回の記事同様、RSpec,FactoryBot and Capybaraは設定済です。
実行するテストケースを限定したいとき
it
の代わりにfit
を使います。
spec_helper.rb
で config.filter_run_when_matching :focus
と設定しているため、各テストの頭文字にf
をつけることで、focus: true
を設定できます。
別タブで開いたページをテストしたいとき
リンクをクリックし、遷移したページ先の項目についてテストしたいときは、within_window(windows.last)
で最後に開いたタブを指定します。
it 'Project詳細からTask一覧ページにアクセスした場合、Taskが表示されること' do visit project_path(project) click_link 'View Todos' #これでページ遷移する。 within_window(windows.last)do #以下、遷移先の内容 expect(page).to have_content task.title expect(Task.count).to eq 1 expect(current_path).to eq project_tasks_path(project) end end
withinメソッド
Capybaraのwithin
メソッドは、ページ内の特定のエリアやアクションを指定できます。
within_window
はウィンドウ画面の指定。
確認画面のページ操作したいとき
何かの項目を削除するとき、「本当に削除しますか?」とアラートメッセージがでますが、それの操作は以下のようにします。
#許可するとき page.driver.browser.switch_to.alert.accept #拒否するとき page.driver.browser.switch_to.alert.dismiss
ApplicationHelperで定義したメソッドをRSpecで使いたいとき
何も考えず使おうとしたら、使えませんでした。私はこういうところをよく忘れるので注意します。
当たり前ですが、include
で読み込む必要があります。
今回は、systemspecファイルでrequire 'rails_helper'
しているので、spec/rails_helper.rb
内でapplication_helper.rb
を読み込む設定をします。
config.include ApplicationHelper #追加
これで、ApplicationHelperで定義したメソッドshort_time
がRspecでも使用できます。
it 'Taskを編集した場合、一覧画面で編集後の内容が表示されること' do fill_in 'Deadline', with: Time.current #今の時刻にdeadlineを更新する。 click_button 'Update Task' click_link 'Back' expect(find('.task_list')).to have_content(short_time(task.reload.deadline)) #ここ expect(current_path).to eq project_tasks_path(project) end
Updateのテストがうまくいかない。値が更新されないとき
上のコードの解説です。
最初、short_time(task.deadline)
としたら、更新前の日時しか取得できなかった。更新出来てないってこと?
謎だったけど、その場しのぎでTime.current
にしてしまいました。
そしてちゃんと調べたら、ドンピシャな記事がありました。
DBの値が更新されても、インスタンスの値は更新されない。task.reload.deadline
とする。
これでテストが通りました。
rspecでupdateのテストを通せない初心者が疑うべきこと - Qiita
findメソッド
Capybaraのfind
メソッドは特定の要素を指定できます。
さきのコード例では、find('.task_list')
で、viewで定義しているclass = "task_list"
を指定しています。
strftimeメソッド
Rubyのメソッドです。
時刻を format 文字列に従って文字列に変換した結果を返します。
Time#strftime (Ruby 2.7.0 リファレンスマニュアル)
以下のように使います。
expect(page).to have_content(Time.current.strftime('%Y-%m-%d'))
FactoryBotのtraitを使う
属性をいろいろ指定して定義したいとき、trait
を使います。
テスト内で何度も同じ属性を指定して呼び出していると、コードの重複が生じてしまいます。
traitを使えばスッキリとした見た目になります。
また、添付ファイルを予めセットすることもできます。(やり方は『everyday Rails』P174 )
#trait定義前 let!(:task) {create(:task, project_id: project.id, status: :done, completion_date: Time.current.yesterday)} #trait定義後 let!(:task) { create(:task, :done) }
FactoryBot.define do factory :task do sequence(:title, "title_1") status { rand(2) } from = Date.parse("2019/08/01") to = Date.parse("2019/12/31") deadline { Random.rand(from..to) } association :project trait :done do #ここ status { :done } completion_date { Time.current.yesterday } end end end
Capybaraのメソッドについて
公式は例もあって分かりやすい
GitHub - teamcapybara/capybara: Acceptance test framework for web applications
【RSpec】System Spec
- やりたいこと
- 前提
- 実装の流れ
- Gem導入
- SystemSpecファイルの作成
- ドライバの設定
- モジュールの設定
- タグの設定
- SystemSpecファイルの設定
- Rspecの実行
- RSpecの結果出力を見易く表示する。
- 参考
やりたいこと
タスクとユーザーのCRUD機能について、テストケースを作成したい。
詰まった部分だけ復習していきます。
前提
実装の流れ
- 必要なgemの導入
- SystemSpecファイルの作成
- ドライバの設定
- モジュールの設定
- SystemSpecファイルの設定
Gem導入
必要なgemを導入
gem 'webdrivers' #ブラウザの挙動を確認 gem 'capybara' #E2Eテスト用フレームワーク
require 'capybara/rspec'
SystemSpecファイルの作成
% rails g rspec:system tasks
※必要に応じて、users
とuser_sessions
も作成。
ドライバの設定
ドライバとは、Capybaraを使ったテスト/Specにおいて、ブラウザ相当の機能を利用するために必要なプログラムです。
『現場で使える Ruby on Rails5 速習実践ガイド』P193
RSpec.configure do |config| config.before(:each, type: :system)do driven_by(:selenium_chrome_headless) #ドライバを一元指定 end
モジュールの設定
今回、ログイン前・ログイン後に分けてテストを実施します。
モジュールにログイン処理を設定しておきます。
そうすれば、各Specで呼び込むことができるので、コードがすっきりします。
どこにファイルを置くか迷ったのですが、support
ディレクトリ内にします。
module LoginModule def login(user) visit login_path fill_in 'Email', with: user.email fill_in 'Password', with: 'password' click_button 'Login' end end
モジュールを作っただけでは使用できないので、include
する必要があります。
config.include FactoryBot::Syntax::Methods config.include LoginModule #追記
そしてrails_helper.rb
を読み込むために、各Specファイルには、上部にrequire 'rails_helper'
があります。
require 'rails_helper'
モジュールを読みこめない…
以上のように設定してから、SystemSpecファイル内でlogin
メソッドを呼び出そうとしたのですが、読み込めませんでした…
includeもOK requireもOKなのに、なぜ・・・?
An error occurred while loading ./spec/system/users_spec.rb. Failure/Error: config.include LoginModule
パスを通してなかった。spec/support
以下を読み込むように設定する。
以下のコメントアウトを外して有効化します。
# The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } #コメントアウトを外す
タグの設定
タグの機能を使うと、タグを設定したテストだけ実行してくれます。
:focus
タグを付けたテストが何もないときは、全てのテストを実行します。
# This allows you to limit a spec run to individual examples or groups # you care about by tagging them with `:focus` metadata. When nothing # is tagged with `:focus`, all examples get run. RSpec also provides # aliases for `it`, `describe`, and `context` that include `:focus` # metadata: `fit`, `fdescribe` and `fcontext`, respectively. config.filter_run_when_matching :focus #コメントアウトを外す。
SystemSpecファイルの設定
全部書いてると大量になってしまうので、詰まった部分だけ見直していきます。
require 'rails_helper' RSpec.describe "Users", type: :system do let(:user) { create(:user) } let(:other_user) { create(:user) } describe 'ログイン前' do describe 'ユーザー新規登録' do context 'フォームの入力値が正常' do it 'ユーザーの新規作成が成功する' end context 'メールアドレスが未入力' do it 'ユーザーの新規作成が失敗する' end context '登録済のメールアドレスを使用' do it 'ユーザーの新規作成が失敗する' do existed_user = create(:user) visit sign_up_path fill_in 'Email', with: existed_user.email fill_in 'Password', with: 'password' fill_in 'Password confirmation', with: 'password' click_button 'SignUp' expect(page).to have_content '1 error prohibited this user from being saved' expect(current_path).to eq users_path expect(page).to have_content("Email has already been taken") expect(page).to have_field 'Email', with: existed_user.email #登録を受け付けなくても、入力したメールアドレスが残ってる。 end end end end describe 'ログイン後' do before { login(user) } #設定したモジュールのloginメソッドを呼び出す describe 'ユーザー編集' do context 'フォームの入力値が正常' do it 'ユーザーの編集が成功する' end context 'メールアドレスが未入力' do it 'ユーザーの編集が失敗する' end context '登録済みのメールアドレスを使用' do it 'ユーザーの編集が失敗する' do visit edit_user_path(user) fill_in 'Email', with: other_user.email fill_in 'Password', with: 'password' fill_in 'Password confirmation', with: 'password' click_button 'Update' expect(page).to have_content('1 error prohibited this user from being saved') expect(page).to have_content("Email has already been taken") expect(current_path).to eq user_path(user) end end context '他ユーザーの編集ページにアクセス' do it '編集ページへのアクセスが失敗する' do visit edit_user_path(other_user) #userでログインしている。other_userの編集ページへアクセスする expect(current_path).to eq user_path(user) expect(page).to have_content("Forbidden access.") end end end describe 'マイページ' do context 'タスクを作成' do it '新規作成したタスクが表示される' do create(:task, title:'test', status: :doing, user: user) visit user_path(user) expect(page).to have_content('You have 1 task.') expect(page).to have_content('test') expect(page).to have_content('doing') expect(page).to have_link('Show') expect(page).to have_link('Edit') expect(page).to have_link('Destroy') end end end end end
require 'rails_helper' RSpec.describe "Tasks", type: :system do let(:user) { create(:user) } #taskとuserは紐付いているので、両方作成する。 let(:task) { create(:task) } describe 'ログイン前' do describe 'ページの遷移確認' do context 'タスクの新規登録ページにアクセス' do it '新規登録ページのアクセス失敗' end context 'タスクの編集ページにアクセス' it '編集ページのアクセス失敗' end context 'タスクの一覧ページにアクセス' do it '全てのユーザーのタスク情報が表示される' do task_list = create_list(:task, 3) visit tasks_path expect(page).to have_content task_list[0].title expect(page).to have_content task_list[1].title expect(page).to have_content task_list[2].title expect(current_path).to eq tasks_path end end context 'タスクの詳細ページにアクセス' do it 'タスク詳細ページを見られること' do visit task_path(task) expect(current_path).to eq task_path(task) expect(page).to have_content task.title end end end describe 'ログイン後' do before { login(user) } describe 'タスク新規登録' do context 'フォームの入力値が正常' do it 'タスクの新規作成が成功する' do visit new_task_path fill_in 'Title', with: 'test_title' fill_in 'Content', with: 'test_content' select 'doing', from: 'Status' fill_in 'Deadline', with: DateTime.new(2020, 9, 28, 10, 30) click_button 'Create Task' expect(page).to have_content 'Title: test_title' #作成した内容が表示される expect(page).to have_content 'Content: test_content' expect(page).to have_content 'Status: doing' expect(page).to have_content 'Deadline: 2020/9/28 10:30' expect(current_path).to eq '/tasks/1' end end context 'タイトルが空白' do it 'タスクの新規登録が失敗すること' end context '登録済のタイトルを入力' do it 'タスクの新規作成が失敗する' do visit new_task_path other_task = create(:task) fill_in 'Title', with: other_task.title fill_in 'Content', with: 'test_content' click_button 'Create Task' expect(page).to have_content '1 error prohibited this task from being saved' expect(page).to have_content 'Title has already been taken' expect(current_path).to eq tasks_path end end end describe 'タスク編集' do let!(:task) { create(:task, user: user) } #letの遅延読み込みではなく、各テストが読み込まれる前に作成する。 let!(:other_task) { create(:task, user: user) } before { visit edit_task_path(task) } #編集ページに遷移しておく。 context 'フォームの入力値が正常' do it 'タスクの更新に成功すること' do fill_in 'Title', with: 'title_test' select :done, from: 'Status' click_button "Update Task" expect(current_path).to eq task_path(task) expect(page).to have_content("Task was successfully updated") end end end describe 'タスク削除' do let!(:task) { create(:task, user: user) } it 'タスクの削除ができること' do visit tasks_path click_link "Destroy" expect(page.accept_confirm).to eq 'Are you sure?' expect(current_path).to eq tasks_path expect(page).to have_content("Task was successfully destroyed") expect(page).not_to have_content task.title #削除したタスクが表示されてないことを確認 end end end end
create_listで連続するテストデータを作成する。
FactoryBot.create_list(:task,3)
FactoryBot
を省略できる設定をしてるのでこのように書ける。
task_list = create_list(:task, 3)
letとlet!の違い
let
は遅延読み込みなので、userやtaskが必要になったときだけ作成します。
一方let!
は、各テストのブロックが実行される前に作成します。
今回、編集ページのテストを行うには、予めタスクが必要なため、let!
で作成しておきます。
let!(:task) { create(:task, user: user) } let!(:other_task) { create(:task, user: user) } before { visit edit_task_path(task) }
エラーが起こったところ
describe 'マイページ' do context 'ログインしていない状態' do it 'マイページへのアクセスが失敗する' do visit user_path expect(current_path).to eq login_path end end end
1) Users ログイン前 マイページ ログインしていない状態 マイページへのアクセスが失敗する Failure/Error: visit user_path ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"users"}, missing required keys: [:id]
[:id]
がないよって怒られる。
user_path(user)
が抜けてた。
(user)
忘れがちなので注意する。
Rspecの実行
% bundle exec rspec spec/system/tasks_spec.rb
RSpecの結果出力を見易く表示する。
以下のオプションを追記する。
--require spec_helper --format documentation #追記
参考
『現場で使える Ruby on Rails5 速習実践ガイド』P184
『everyday Rails』
【RSpec】モデルスペック
前提
RSpecのセットアップ済
FactoryBot導入済
実現したいこと
既存のTaskモデルのバリデーションをチェックしたい
文法に馴染みがなく、簡単なテストなはずなのに半日くらいかかってしまいました。
分かりにくかった部分だけ、復習していきます。
モデルスペックを作成する
% rails g rspec:model task
FactoryBotの設定
UserとTaskは1対多の関係です。なので、どちらもテスト用データを作成します。
email
とtitle
はユニーク制約をつけています。
FactoryBot.define do factory :user do sequence(:email) { |n| "tester#{n}@example.com" } password { 'password' } password_confirmation { 'password' } end end
一意である必要があるemailについては、sequence
メソッドを使います。
この構文で、ユーザーを生成する度にn
の数字を変えて一意のアドレスを作成できます。
FactoryBot.define do factory :task do sequence(:title, "title_1") content { 'Content' } status { 'todo' } #enumで定数を設定している。 deadline { Date.current.tomorrow } association :user end end
先程と同様、title
はユニーク制約をつけているので、sequence
メソッドを使用します。
最初は、sequence(:title) { |n| "Title #{n}" }
のようにしていたんですが、第二引数を渡すと.next
を呼んでくれて、連番をつけてくれるようです。便利。
String#next (Ruby 2.7.0 リファレンスマニュアル)
FactoryBot (旧FactoryGirl) の sequence と .next - Qiita
deadline { 1.week.from_now }
こっちのほうがいいかも
こんな機能があるのは知らなかった。。
Active Support コア拡張機能 - Railsガイド
association :user
これで、TaskとUserに関連があることを設定できる。便利。
FactoryBot省略設定
FactoryBotは通常、FactoryBot.create(略)
のようにテストデータを生成できますが、以下の設定をしておくと、FactoryBot
を省略できます。
RSpec.configure do |config| (略) config.include FactoryBot::Syntax::Methods #追記 end
Task Model Spec
テストデータ生成の準備が整ったので、Specのコードを書いていきます。
とりあえず今回は、以下の3つに絞ります。
- 全部の属性を登録したときvalid
- titleがないときにinvalid
- titleが重複したときinvalid
require 'rails_helper' RSpec.describe Task, type: :model do describe 'validation' do it "is valid with all attributes" do task = build(:task) #先の設定のおかげで、FactoryBot.build(:task)と書かなくてもOK expect(task).to be_valid expect(task.errors).to be_empty #エラーメッセージなし。 end it "is invalid without a title" do task_without_title = build(:task, title: nil) #titleはなるべくわかりやすくする。 expect(task_without_title).to be_invalid expect(task_without_title.errors[:title]).to eq ["can't be blank"] end it "is invalid with a duplicate title" do task = create(:task) #1つめのtaskはcreateで保存までする。 task_with_duplicated_title = build(:task, title: task.title) #2つめのtaskは1つめと同じ名前にする。 expect(task_with_duplicated_title).to be_invalid expect(task_with_duplicated_title.errors[:title]).to eq ["has already been taken"] end
全てに共通する流れは、
- Taskオブジェクトを生成
- 期待する挙動を検査