ActiveSupport_TestCase + shoulda によるテスト のバックアップソース(No.5)
更新- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- バックアップ を表示
- ソフトウェア/rails/ActiveSupport_TestCase + shoulda によるテスト へ行く。
[[公開メモ]] #contents * ActiveSupport::TestCase + shoulda による Ruby on Rails のテスト [#ge5f5cd1] - ActiveSupport::TestCase は Rails 標準のテストフレームワーク - [[shoulda>https://github.com/thoughtbot/shoulda]] は読みやすいテストケースを書くための拡張 これらを使って Rails アプリケーションをテストするためのノウハウをメモとして残したい * 環境 [#ab825549] 現在のこちらの環境は以下の通りです。 -rails 4.0.4 -minitest 4.7.5 -shoulda 3.5.0 * いろいろ検討中 [#rc1df26c] まだちゃんとしたテストは書き始めたばかりなので全然やり方が定まってません。 このレベルの物をこんなところに晒すのも何なのですが、とりあえずメモとして書いちゃいます。 ** 便利な gem [#d7228abc] - development 用 :better_errors+binding_of_caller| エラー画面でコールスタック上の変数値を参照するなどいろいろできるようになる :tapp| 昔ながらの puts によるデバッグに於いて caller チェーンを切らずにデバッグ出力するのに便利 - test 用 :shoulda|読みやすいテストケースを書けるようにする :database_rewinder|データベースを高速に初期化する :tapp|上記参照 Gemfile group :development do gem 'better_errors' gem 'binding_of_caller' gem 'tapp' end group :test do gem 'database_rewinder' gem 'shoulda' gem 'tapp' end ** shoulda の基本 [#w7e40d51] shoulda では、 - context の setup で操作を行い - should で検証を行う - テストの内容説明は context + should で自動生成される というのが基本になります。 生の ActiveSupport::TestCase とは考え方が異なるので始めは戸惑います。 ActiveSupport::TestCase であれば、 - setup ですべての test に共通の前処理を行い - 個々の test で操作を行い、さらに assert も行う - テストの内容説明は test の名前 + assert の記述で行う となります。 shoulda の場合は「個々の should の中で操作を行わない」 という点が大きな違いなわけです。 無理矢理対応関係を書けば次のようになります。 ||~ shoulda |~ assert | |~操作 | context #1| setup| |~| context #2|~| |~| context #3| test| |~検証 | should| assert| 下で言えば、 LANG:ruby [:ja,:en].each do |locale| context "with locale = #{locale}," do setup { # ロケール設定 } context "with auto-login," do setup { # オートログイン準備 } context 'GET #home' do setup { get :home # 実際の操作 } should 期待される結果1 should 期待される結果2 end end end などとなっています。 これで、 - with locale ja, with auto-login, GET #home should 期待される結果1 - with locale ja, with auto-login, GET #home should 期待される結果2 - with locale en, with auto-login, GET #home should 期待される結果1 - with locale en, with auto-login, GET #home should 期待される結果2 のようにすべてのテストに適切な説明が付きます。 エラーメッセージの例: LANG:console ... 1) Failure: AccountControllerTest#test_: with locale en, without login, valid POST #create should send email to registered address. [/home/osamu/MyApp/test/controllers/account_controller_test.rb:141]: Expected true to be nil or false ... should の中に get :home を書いてしまうと、 should にうまく名前を付けられなくて困ることになります。 - context の setup は、中に含まれるすべての should に対して毎回呼ばれます。 - すべての should に先駆けて一回だけ行いたいような初期化処理がもしあれば、 テストケースの宣言部分(クラススコープ)で行えば良いのだと思います。 ただ、個々のテストは独立していないといろいろややこしいので、 そういう初期化が必要になることはあまり無いんじゃないかとも思います。 ** コントローラのテスト [#m9c4f2cd] 途中ロケールの処理が入っていますが、そのあたりの構成は [[ソフトウェア/rails/国際化]] に書いた通りです。 test/controllers/account_controller_test.rb LANG:ruby(linenumber) #coding: utf-8 require 'test_helper' require_relative '../models/_test_params.rb' class AccountControllerTest < ActionController::TestCase tests AccountController # この例のように標準的な命名をしている限り、 # コントローラ名の指定は省略してもよい # Params::param_name の形で Hash 形式のモデル作成用 # パラメータを参照可能にする # Params::param_name(key1: value1, key2: value2) として # いくつかのキーをオーバーライドすることも可能 include TestParams # setup とテストケース should の中とで情報をやりとりするのに # 普通にインスタンス変数 @xxx を使うか、反則技でクラス変数 @@xxx を # 使うか迷い中 # # インスタンス変数を使うのが普通だが、 # - クラススコープからインスタンス変数にアクセスできなくて # 残念に感じることがたまにある # - 比較的多数のインスタンス変数がフレームワークによって # 使われているため、命名の衝突が怖い # # クラス変数を使うのはかなり異端 # - 本来の目的からずれまくってるので完全に反則技 # - 初期化し忘れると直前のテストケースの値が残ってしまうのも問題 # - その代わり、どこからでもアクセスできるのがかなり便利? # ActionController::TestCase にて、以下のクラス変数が使われている。 # # @@required_fixture_classes: false # @@already_loaded_fixtures: {} # @@test_suites: {ActiveSupport::TestCase=>true, ...} # @@current: #<AccountControllerTest:0xb192174> # # クラス変数を使う場合は、これらおよび未来の TestCase のクラス変数と # 名前が被らないよう注意する必要あり # インスタンス変数として以下が定義されるので、名前が # 被らないように注意が必要 # # インスタンス変数 利用時の記法 内容 # @__name__ @__name__ 現在実行中のテストのタイトルが入る # @__io__ # @passed # @_assertions # @fixture_cache # @fixture_connections # @loaded_fixtures # @tagged_logger # @_partials # @_templates # @_layouts # @_files # @request request #<ActionController::TestRequest> # @response response #<ActionController::TestResponse> # @controller @controller #<Controller under testing> # @routes @routes #<ActionDispatch::Routing::RouteSet> # @shoulda_context # @html_document html_document #<HTML::Document> # @assigns assigns @controller 内で定義されたインスタンス変数 # すべての should に先駆けて1回だけ行いたい処理がもしもあれば # ここに書けばいい(通常あまり必要なさそう?) # 初期化処理1 # 初期化処理2 # すべての context に共通の初期化処理 # これは個々の should の直前に、毎回呼ばれることになる setup { clear_all } def clear_all ActionMailer::Base.deliveries.clear DatabaseRewinder.clean_all session.clear cookies.clear end # 以下すべてを ja, en 両方のロケールで試す [:ja,:en].each do |locale| context "with locale #{locale}," do setup { request.env['HTTP_ACCEPT_LANGUAGE'] = locale.to_s } # ここからが本当のテスト内容 context "without login," do context 'GET #home' do setup { get :home } should redirect_to_path :account_login should set_the_flash # login first should 'set session_intended_path to requested path' do # セッションキーは ApplicationController で # @session_intended_path="hogehoge" # として与えられているので、assigns を使って読み取る assert_equal @request.path, session[assigns(:session_intended_path)] end end context "GET #new" do setup { get :new } should respond_with :success should render_template :new should_not set_the_flash end context 'invalid POST #create' do setup { post :create, account: Params::invalid_account } should "not create account" do assert_equal 0, Account.count end should render_template :new end context 'valid POST #create' do setup { post :create, account: Params::valid_account } should "create account" do assert_equal 1, Account.count end should set_the_flash # account created should redirect_to_path :account_login should "send email to registered address" do assert_not ActionMailer::Base.deliveries.empty? email_sent = ActionMailer::Base.deliveries.last assert_equal Params::valid_account[:email], email_sent.to end end end context "with auto-login," do setup { @@account = Account.create!( Params::valid_account( password: Account.generate_new_password, auto_login: Account.generate_new_password ) ) cookies[assigns(:cookie_auto_login)] = [@@account.auto_login, @@account.email].join(':') } context 'GET #home' do setup { get :home } should respond_with :success should 'update autologin password at successful auto login' do assert_not_equal @@account.auto_login, assigns[:account].auto_login assert_equal assigns[:account].auto_login, cookies[assigns[:cookie_auto_login]].split(':',2)[0] end end context 'GET #new' do setup { get :new } should set_the_flash # already login should redirect_to_path :account end context 'GET #edit' do setup { get :edit } should respond_with :success should render_template :edit end context 'POST #update' do setup { post :update, account: {name: "Updated Name"} } should 'update name' do account = Account.find_by_email(@@account.email) assert_equal "Updated Name", account.name end should set_the_flash # successfully updated should redirect_to_path :account end context 'DELETE #destroy' do setup { delete :destroy } should "delete account" do assert_equal 0, Account.count end should set_the_flash # successfully deleted should redirect_to_path :default end context 'with valid registration,' do setup { @@account.registration = Registration.create! Params::valid_registration } context 'DELETE #destroy' do setup { delete :destroy } should "not delete account" do assert_equal 1, Account.count end should set_the_flash # can not delete should redirect_to_path :account end end end end end end * モデル生成用パラメータ [#c095e6b7] test/models/_test_params.rb LANG:ruby(linenumber) # coding: utf-8 module TestParams module Params # # name で指定される名前のクラス関数を作成する # Params::param_name = ハッシュを返す # Params::param_name(key: value) = ハッシュに hash[:key]=value してから返す # def self.param(param_name, hash) class_eval <<-EOS def self.#{param_name}(options={}) #{hash.inspect}.merge(options) end EOS end param :valid_account, { name: "Foo Baa", email: "account@example.com", affiliation: "My institute", address: "My address", telephone: "+00-000-0000" } param :invalid_account, { name: '' } param :valid_registration, { gender: 1, membership: 1, student: false, receipt_address: 'My address', payaccount: 'My Account' } param :invalid_registration, { gender: 1, membership: 1, student: false, receipt_address: nil, payaccount: nil } end end * 独自拡張を読み込む [#xdb96442] test/test_helper.rb LANG:ruby ... # I18n.translate の翻訳の抜けを検出する require 'test_i18n_translation_strictly' # shoulda 用のマクロを読み込む Dir[File.expand_path('../shoulda_macros', __FILE__) << '/*.rb'].each do |file| require file end ... ** I18n.translate の翻訳の抜けを検出する [#ub2cc8f0] config/initializers/test_i18n_translation_strictly.rb LANG:ruby(linenumber) # coding: utf-8 # 翻訳が見付からないときに例外を投げる # I18n.exception_handler # http://guides.rubyonrails.org/i18n.html # これだけだとモデル名やカラム名の翻訳が無くても # エラーにならないので、 # ActiveRecord::model_name.human # https://github.com/rails/rails/blob/master/activemodel/lib/active_model/translation.rb#L43 # ActiveRecord::human_attribute_name # https://github.com/rails/rails/blob/master/activemodel/lib/active_model/naming.rb#L175 # にも手を入れる。locale == :en では、モデル名やカラム名を # そのまま使えることも多いので、それ以外の時に翻訳が未設定 # であれば例外を投げる # # 後者2つについては関数のコードをほぼ丸々コピーしているので、 # 元のコードが変更されたらそれに合わせて変更しなければならない module I18n class JustRaiseExceptionHandler < ExceptionHandler def call(exception, locale, key, options) if exception.is_a?(MissingTranslation) and key.to_s != 'i18n.plural.rule' raise exception.to_exception else super end end end end I18n.exception_handler = I18n::JustRaiseExceptionHandler.new module ActiveModel class Name def human(options={}) return @human unless @klass.respond_to?(:lookup_ancestors) && @klass.respond_to?(:i18n_scope) defaults = @klass.lookup_ancestors.map do |klass| klass.model_name.i18n_key end defaults << options[:default] if options[:default] # defaults << @human defaults << @human if I18n.locale == :en options = { scope: [@klass.i18n_scope, :models], count: 1, default: defaults }.merge!(options.except(:default)) I18n.translate(defaults.shift, options) end end end module ActiveModel module Translation def human_attribute_name(attribute, options = {}) options = { count: 1 }.merge!(options) parts = attribute.to_s.split(".") attribute = parts.pop namespace = parts.join("/") unless parts.empty? attributes_scope = "#{self.i18n_scope}.attributes" if namespace defaults = lookup_ancestors.map do |klass| :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}" end defaults << :"#{attributes_scope}.#{namespace}.#{attribute}" else defaults = lookup_ancestors.map do |klass| :"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}" end end defaults << :"attributes.#{attribute}" defaults << options.delete(:default) if options[:default] # defaults << attribute.humanize defaults << attribute.humanize if I18n.locale == :en options[:default] = defaults I18n.translate(defaults.shift, options) end end end ** should redirect_to_path [#r6fcffc4] test/shoulda_macros/action_controller_macros.rb LANG:ruby(linenumber) class ActionController::TestCase # shoulda の redirect_to では、 # should redirect_to account_path # should redirect_to account_path(action: :edit) # should redirect_to url_for(controller: :account, action: :edit) # などとは書けないので、かわりに # should redirect_to("account_path"){ account_path } # should redirect_to("account_path(action: :edit)"){ account_path(action: :edit) } # should redirect_to("controller: :account, action: :edit"){ # @controller.url_for(controller: :account, action: :edit) } # などと書かなければならない。より簡単に # should redirect_to_path :account # should redirect_to_path :account, action: :edit # should redirect_to_path controller: :account, action: :edit # などと書けるようにするのがこの関数 def self.redirect_to_path(*arg) if arg.first.is_a? Symbol redirect_to_path_arg = arg.dup redirect_to_path_symbol = :"#{redirect_to_path_arg.shift}_path" if redirect_to_path_arg.empty? redirect_to(redirect_to_path_symbol){ __send__(redirect_to_path_symbol) } else redirect_to_path_arg_s =redirect_to_path_arg.inspect.gsub(/\A\[|\]\z/,'') redirect_to_path_arg_s.gsub!(/\A\{|\}\z/,'') if redirect_to_path_arg.count == 1 redirect_to("#{redirect_to_path_symbol}(#{redirect_to_path_arg_s})", &Proc.new { __send__(redirect_to_path_symbol, *redirect_to_path_arg) }) end else redirect_to_path_arg = arg.dup redirect_to_path_arg_s =redirect_to_path_arg.inspect.gsub(/\A\[|\]\z/,'') redirect_to_path_arg_s.gsub!(/\A\{|\}\z/,'') if redirect_to_path_arg.count == 1 redirect_to(redirect_to_path_arg_s){ @controller.url_for *redirect_to_path_arg } end end end *** context_for_all_locales [#r1d0c88f] というのも作っておくと良さそう ** エラーの生じた should を特定しやすくする [#wc579ebc] shoulda からのエラーメッセージが以下のように shoulda ライブラリや shoulda macro の中を指してしまうことがあります。 LANG:console 1) Failure: AccountControllerTest#test_: with locale en, without login, invalid POST #create should set the flash. [/var/lib/gems/2.0.0/gems/shoulda-context-1.2.1/lib/shoulda/context/context.rb:344]: Expected the flash to be set, but no flash was set このようなメッセージからエラーが生じたテストの場所を特定するのはなかなか手間がかかります。 ごたごたは飛ばして解決策へ飛ぶ場合は [[こちら>#s29326e1]] *** 試行錯誤 [#c418a6e5] で、例外のバックトレースをいじって改善できない物か???と考えたわけです。 ActiveSupport::TestCase ~ https://github.com/rails/rails/blob/master/activesupport/lib/active_support/test_case.rb gem 'minitest' を取り込んで ::Minitest::Test を継承している。ruby 標準の minitest ではないらしい? これかな?~ https://github.com/seattlerb/minitest capture_exceptions で self.failures にエラーを溜めて、~ https://github.com/seattlerb/minitest/blob/master/lib/minitest/test.rb#L202 to_s で文字に直す?~ https://github.com/seattlerb/minitest/blob/master/lib/minitest/test.rb#L261 場所を表わす部分は location で、self.failure というのがどこから来るのか分からないものの、 アサート例外の location から得ているように見える~ https://github.com/seattlerb/minitest/blob/master/lib/minitest/test.rb#L224 failure は Runnable で定義されていた → 何のことはない failures.first のことだった~ https://github.com/seattlerb/minitest/blob/master/lib/minitest.rb#L345 Minitest::Assertion 例外の location はここ~ https://github.com/seattlerb/minitest/blob/master/lib/minitest.rb#L633 LANG:ruby module Minitest VERSION = "5.3.4" # :nodoc: ... class Assertion < Exception ... def location last_before_assertion = "" self.backtrace.reverse_each do |s| break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/ last_before_assertion = s end last_before_assertion.sub(/:in .*$/, "") end この backtrace を遡っている部分の条件を変更してやれば良い・・・のかと思ったら違うみたいです??? どこかで間違った??? ああ、動かしてるバージョンが段違いでした orz~ 上記 seattlerb/minitest でメンテしているのは 5.x.x 系列で、4.x.x 系列は 4.7.2 あたりが最後? 古いコードだと、普通に rescue で捕まえて runner.puke してますね~ https://github.com/seattlerb/minitest/blob/7e25922c340fa1cfdbeffaf8f37ec020cf6a4fff/lib/minitest/unit.rb#L1255 puke は location を呼んでるから、~ https://github.com/seattlerb/minitest/blob/7e25922c340fa1cfdbeffaf8f37ec020cf6a4fff/lib/minitest/unit.rb#L954 この location を書き換えればいいのかと思ったのですが・・・ LANG:ruby module Minitest class Unit def location e e.backtract.join("\n") end end end として得られたトレースはこんな感じ。 LANG:console minitest-4.7.5/lib/minitest/unit.rb:195:in `assert' shoulda-context-1.2.1/lib/shoulda/context/assertions.rb:91:in `safe_assert_block' shoulda-context-1.2.1/lib/shoulda/context/assertions.rb:83:in `assert_rejects' shoulda-context-1.2.1/lib/shoulda/context/context.rb:358:in `block in should_not' shoulda-context-1.2.1/lib/shoulda/context/context.rb:413:in `instance_exec' shoulda-context-1.2.1/lib/shoulda/context/context.rb:413:in `block in create_test_from_should_hash' minitest-4.7.5/lib/minitest/unit.rb:1258:in `run' minitest-4.7.5/lib/minitest/unit.rb:933:in `block in _run_suite' minitest-4.7.5/lib/minitest/unit.rb:926:in `map' minitest-4.7.5/lib/minitest/unit.rb:926:in `_run_suite' minitest-4.7.5/lib/minitest/parallel_each.rb:71:in `block in _run_suites' minitest-4.7.5/lib/minitest/parallel_each.rb:71:in `map' minitest-4.7.5/lib/minitest/parallel_each.rb:71:in `_run_suites' minitest-4.7.5/lib/minitest/unit.rb:877:in `_run_anything' minitest-4.7.5/lib/minitest/unit.rb:1085:in `run_tests' minitest-4.7.5/lib/minitest/unit.rb:1072:in `block in _run' minitest-4.7.5/lib/minitest/unit.rb:1071:in `each' minitest-4.7.5/lib/minitest/unit.rb:1071:in `_run' minitest-4.7.5/lib/minitest/unit.rb:1059:in `run' minitest-4.7.5/lib/minitest/unit.rb:795:in `block in autorun' バックトレースに TestCase の行が含まれていない??? https://github.com/thoughtbot/shoulda-context/blob/master/lib/shoulda/context/context.rb#L356 LANG:ruby def should_not(matcher) name = matcher.description blk = lambda { assert_rejects matcher, subject } self.shoulds << { :name => "not #{name}", :block => blk } end この lambda で作った blk が shoulds に溜められて、後から順に実行され、そこでエラーが出ると。 これだと例外から行番号を得るのは難しいですね。 *** 解決 [#s29326e1] むしろ、この :name に行番号を突っ込んじゃえばいいという話かも。 test/shoulda_macros/add_caller_information.rb LANG:ruby #coding: utf-8 module Shoulda module Context class Context # :nodoc: # should ブロックの :name に行番号を書き足す def add_caller_line_number(should) # 現バージョンでは2番目が呼び出し元になるみたい caller(2).first =~ /:(\d+):in / should[:name] = should[:name].to_s + " (at line #{$1})" end alias :should_without_caller_information :should def should(name_or_matcher, options = {}, &blk) count_before = self.shoulds.count should_without_caller_information(name_or_matcher, options = {}, &blk) if self.shoulds.count > count_before add_caller_line_number(self.shoulds.last) else add_caller_line_number(self.should_eventuallys.last) end end alias :should_not_without_caller_information :should_not def should_not(matcher) should_not_without_caller_information(matcher) add_caller_line_number(self.shoulds.last) end end end end これで、 LANG:console 3) Failure: AccountControllerTest#test_: with locale ja, without login, GET #home should not set the flash (at line 35). [/var/lib/gems/2.0.0/gems/shoulda-context-1.2.1/lib/shoulda/context/context.rb:358]: Did not expect the flash to be set, but was #<ActionDispatch::Flash::FlashHash:0xad041fc ... などというように、should や should_not に対応する行番号が (at line xxx) の形で付くようになりました。 caller() は遅いので、called_from を作った、という記事がありました。テストの規模が大きい場合には検討の価値があるかもしれません。(このライブラリ、Ruby 1.8 以外に対応していない?)~ http://magazine.rubyist.net/?0031-BackTrace#l7 ~ とはいえ1万個の should を作ってようやく1秒違うかどうかなので、気にするだけ損なのかも。 * assert_XXX [#h14dab47] |>|>|>|>|~mt の実装する基本機能| |名前|引数|テスト内容|refuse|実装| | assert |test, msg = nil | test | ○ |mt| | assert_accepts | matcher, target, options = {} | matcher.matches?(target) | | sd | | assert_blank | obj, msg=nil | obj.blank? | | AS | | assert_contains | collection, x, extra_msg = "" | x can be regexp | | sd | | assert_deprecated | match = nil, &block | | | AS | | assert_difference|expression, diff = 1, msg = nil, &block|block 前後で変化||AS| | assert_does_not_contain | collection, x, extra_msg = "" | x can be regexp | | sd | | assert_dom_equal | expected, actual, message = "" | HTML::Document.new(?).rootを比較 | | AD | | assert_dom_not_equal | expected, actual, message = "" | HTML::Document.new(?).rootを比較 | | AD | | assert_empty |obj, msg = nil | obj.empty? | ○ |mt| | assert_equal |exp, act, msg = nil | exp == act | ○ |mt| | assert_generates | expected_path, options, defaults={}, extras = {}, message=nil | | | AD | | assert_in_delta |exp, act, delta = 0.001, msg = nil | (act-exp).abs <= delta | ○ |mt| | assert_in_epsilon |a, b, epsilon = 0.001, msg = nil | 上の別名 | ○ |mt| | assert_includes |collection, obj, msg = nil | collection.include?(obj) | ○ |mt| | assert_instance_of |cls, obj, msg = nil | obj.instance_of?(cls) | ○ |mt| | assert_kind_of |cls, obj, msg = nil | obj.kind_of?(cls) | ○ |mt| | assert_match |matcher, obj, msg = nil | matcher =~ obj | ○ |mt| | assert_nil |obj, msg = nil | obj.nil? | ○ |mt| | assert_no_difference|expression, msg = nil, &block|block 前後で保存||AS| | assert_no_match |matcher, obj, msg = nil | matcher !~ obj | |mt| | assert_no_tag | *opts | html_document.find(*opts) | | AD | | assert_not |obj, msg = nil|!obj||AS| | assert_not_deprecated | &block | | |AS| | assert_not_empty |obj, msg = nil | !obj.empty? | |mt| | assert_not_equal |exp, act, msg = nil | exp != act | |mt| | assert_not_in_delta |exp, act, delta = 0.001, msg = nil | (act-exp).abs > delta | |mt| | assert_not_in_epsilon |a, b, epsilon = 0.001, msg = nil | 上の別名 | |mt| | assert_not_includes |collection, obj, msg = nil | !collection.include?(obj) | |mt| | assert_not_instance_of |cls, obj, msg = nil | !obj.instance_of?(cls) | |mt| | assert_not_kind_of |cls, obj, msg = nil | !obj.kind_of?(cls) | |mt| | assert_not_nil |obj, msg = nil | !obj.nil? | |mt| | assert_not_operator |o1, op, o2 = UNDEFINED, msg = nil | !o1._send_(op, o2) | |mt| | assert_not_predicate |o1, op, msg = nil | !o1._send_(op) | |mt| | assert_not_respond_to |obj, meth, msg = nil | !obj.respond_to?(meth) | |mt| | assert_not_same |exp, act, msg = nil | !exp.equal?(act) | |mt| | assert_nothing_raised | | | | | | assert_operator |o1, op, o2 = UNDEFINED, msg = nil | o1._send_(op, o2) | ○ |mt| | assert_output |stdout = nil, stderr = nil | | |mt| | assert_predicate |o1, op, msg = nil | o1._send_(op) | ○ |mt| | assert_raise | | | | | | assert_raises |*exp | | |mt| | assert_recognizes | expected_options, path, extras={}, msg=nil | | | AD | | assert_redirected_to | options = {}, message=nil | | | AD | | assert_rejects | matcher, target, options = {} | !matcher.matches?(target) | | sd | | assert_respond_to |obj, meth, msg = nil | obj.respond_to?(meth) | ○ |mt| | assert_response | type, message = nil | | | AD | | assert_routing | path, options, defaults={}, extras={}, message=nil | | | AD | | assert_same |exp, act, msg = nil | exp.equal?(act) | ○ |mt| | assert_same_elements | a1, a2, msg = nil | compare array asif it is set | | sd | | assert_select | *args, &block | | | AD | | assert_select_email | &block | | | AD | | assert_select_encoded | element = nil, &block | | | AD | | assert_select_jquery | *args, &block | | | jr | | assert_send |[recv, msg, *args], m = nil | recv._send_(msg, *args) | |mt| | assert_silent | |assert_output "", "" | |mt| | assert_tag | *opts | html_document.find(*opts) | | AD | | assert_template | options = {}, message = nil | | | AC | | assert_throws |sym, msg = nil | | |mt| - mt: minitest = https://github.com/seattlerb/minitest/blob/7e25922c340fa1cfdbeffaf8f37ec020cf6a4fff/lib/minitest/unit.rb#L190 - AS: ActiveSupport = https://github.com/rails/rails/blob/4-0-stable/activesupport/lib/active_support/testing/assertions.rb- AS: ActiveSupport deplication = https://github.com/rails/rails/blob/4-1-stable/activesupport/lib/active_support/testing/deprecation.rb - AD: ActionDispatch = https://github.com/rails/rails/tree/4-0-stable/actionpack/lib/action_dispatch/testing/assertions - AC: ActionController = https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_controller/test_case.rb#L95 - sd: Shoulda = https://github.com/thoughtbot/shoulda-context/blob/master/lib/shoulda/context/assertions.rb - jr: jquery-rails = https://github.com/rails/jquery-rails/blob/master/lib/jquery/assert_select.rb AD: with_routing ~ AD: css_select ~ * shouda の matcher [#gd8d5b05] #multicolumns *** [[ActiveModel Matchers>https://github.com/thoughtbot/shoulda-matchers#activemodel-matchers]] [#n7a5004d] -allow_mass_assignment_of -''allow_value'' -ensure_inclusion_of -ensure_exclusion_of -ensure_length_of -have_secure_password -validate_absence_of -validate_acceptance_of -validate_confirmation_of -validate_numericality_of -validate_presence_of -validate_uniqueness_of #multicolumns *** [[ActiveRecord Matchers>https://github.com/thoughtbot/shoulda-matchers#activerecord-matchers]] [#x0e4eaa5] -accept_nested_attributes_for -belong_to -have_many -have_one -have_and_belong_to_many -have_db_column -have_db_index -have_readonly_attribute -serialize #multicolumns *** [[ActionController Matchers>https://github.com/thoughtbot/shoulda-matchers#actioncontroller-matchers]] [#kdd1429f] -filter_param -permit -''redirect_to'' -''render_template'' -''render_with_layout'' -rescue_from -''respond_with'' -route -''set_session'' -''set_the_flash'' -use_after_filter -use_after_action -use_around_filter -use_around_action -use_before_filter -use_around_action #multicolumns(end) 始めからコード側で宣言的に書かれている、例えば has_many なんかを わざわざテストしても、ほとんどコピペになるだけで意味は薄いと思います。 なので、上記のように matcher はたくさんある物の、頻繁に使うのは、 ActionModel の -allow_value ActionController の -redirect_to -render_template -respond_with -set_session -set_the_flash くらいかもしれません。 * 役立ちそうな記事 [#x64e134e] - http://www.railstips.org/blog/archives/2009/02/21/shoulda-looked-at-it-sooner/ * コメント [#yfa709ad] #article_kcaptcha
Counter: 8409 (from 2010/06/03),
today: 2,
yesterday: 0