質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

ArgumentError: wrong number of arguments (given A, expected B)のA,Bの引数の判断の理解がしたいです。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 9,739
退会済みユーザー

退会済みユーザー

情報(FW/ツールのバージョンなど)
言語 ruby
FW ruby on rails

(初心者でなおかつ独学の為、根本的な所から違う場合あります。すみません。
勘違いが多すぎて何処から突っ込めばいいかわからない場合は、とりあえず『エラーの解決方法』と『どう判断したか』のみで大丈夫です。)

前提・実現したいこと

rails test:models実行時に出てくる
ArgumentError: wrong number of arguments (given A, expected B)のA,Bの判断方法が知りたいです。
今までは
A=呼び出し側の引数の数 B=メソッド側の引数の数 
と認識して、このエラー関係は解決していました。
ですがテスト実行時の場合の「呼び出し側の引数」はどこなのかで迷ってしまいました。
なのでちゃんと(given A, expected B)のA,Bの判断方法を教えて頂きたいです。

迷った理由

今までの ArgumentError: wrong number of arguments (given A, expected B)
のエラーでは大体引数を呼びだすフォームなどがありました。そしてそれに関連するメソッドを探して引数の数を調整していました。
ですが今回実行したテストコードの内容は
アドレスのフォーマット関係を入れた配列とeach doメソッド
のみです。
(すぐ下にソースコードも書いています)

考えれば考えるほど疑問点が出てきて、自分の考えがまとまらなくなってきたので
話口調で申し訳ありませんが、今まで考えた事を書いてみました。
出来ればこれも踏まえて、解説して貰えるとありがたいです。

=============================
アドレスのフォーマット関係を入れた配列が呼び出し側の引数なのかな?

でも引数は処理する関数とかメソッドがあって初めて引数って扱うし、
変数のみで引数として扱えるのか?

each doが手前の変数を引数として扱うメソッドだよなあ・・・。
じゃあこれが呼び出しの引数で合ってる?

%w[user@example.com USER@foo.COM A_US-ER@foo.bar.org first.last@foo.jp alice+bob@baz.cn] = valid_addresses = valid_address(each doの仮引数)

て考えたら1個だしgiven側の数字と合ってる。

じゃあB側のメソッド側の引数ってどこだろう???

メールフォーマットの検証のテストって関係ありそうなファイルはデータのバリテーション関係のコードが書いてる
user.rbぐらいしかないし・・・。

一応このファイルも終わりは end だしメソッドとして扱えるのかな?

わからない・・・。
====================

エラーコード

Running:

.....E

Error:
UserTest#test_email_validation_should_accept_valid_addresses:
ArgumentError: wrong number of arguments (given 1, expected 2)
test/models/user_test.rb:41:in test'    test/models/user_test.rb:41:in block in <class:UserTest>'

ソースコード

ファイル名 user_test.rb

test "email validation should accept valid addresses" do
valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
first.last@foo.jp alice+bob@baz.cn]
valid_addresses.each do |valid_address|
@user.email = valid_address
assert @user.valid?, "#{valid_address.inspect} should be valid"
end

ファイル名 user.rb

class User < ApplicationRecord
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
format: { with: VALID_EMAIL_REGEX }
end

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • troch

    2018/12/15 20:31

    test/models/user_test.rb:41 はコードのどこに該当するのか、ご教授願えますでしょうか。

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2018/12/16 00:48

    すみません。分からないなりにでも何とかまとめようとしたのが色々裏目に出てしまった様です。
    users_test.rbのコード全体を載せます。
    require 'test_helper'

    class UserTest < ActiveSupport::TestCase

    def setup
    @user = User.new(name: "Example User", email: "user@example.com")
    end

    test "should be valid" do
    assert @user.valid?
    end

    test "name should be present" do
    @user.name = " "
    assert_not @user.valid?
    end

    test "email should be present" do
    @user.email = " "
    assert_not @user.valid?
    end

    test "name should not be too long" do
    @user.name = "a" * 51
    assert_not @user.valid?
    end

    test "email should not be too long" do
    @user.email = "a" * 244 + "@example.com"
    assert_not @user.valid?
    end

    test "email validation should accept valid addresses" do
    valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
    first.last@foo.jp alice+bob@baz.cn]
    valid_addresses.each do |valid_address|
    @user.email = valid_address
    assert @user.valid?, "#{valid_address.inspect} should be valid"
    end

    test "email addresses be unique" do
    duplicate_user = @user.dup.upcase
    @user.save
    assert_not duplicate_user.valid?
    end
    end
    end

    41は下から7行目の
    test "email addresses be unique" do
    です。

    キャンセル

  • troch

    2018/12/16 09:04

    共有ありがとうございます。回答を期待いたしましたので、お手すきの際にでもご確認くださいますと幸いです。

    キャンセル

回答 1

checkベストアンサー

+1

レシーバとメソッド、引数について整理してみます。  
例えば、メソッドに渡された2つの引数を表示するだけの以下のようなインスタンスメソッドがあったとします。

class User
  def hoge(num1, num2)
    p num1
    p num2
  end
end

@user = User.new
@user.hoge(1, 2)
#=> 1
#=> 2

この場合、@user.hoge(1, 2)は以下のような意味を持つと思います。  

レシーバ メソッド 引数
@user hoge 1(第一引数), 2(第二引数)

レシーバ(Userクラスのインスタンスである@user)に定義されたメソッドhogeには、  
def hoge(num1, num2)と定義してありますので、  
このメソッドを呼び出す際に、引数が2つ必要になります。  
つまりメソッドを呼び出す際に引数が1つだったり、3つだったりした場合はエラーになります。この際に出てくるのが、ArgumentErrorだと思います。  

# 引数が2つ欲しいのに、1つしか渡されていないというエラーメッセージ
@user.hoge(1) #=> wrong number of arguments (given 1, expected 2) (ArgumentError)

# 引数が2つ欲しいのに、3つ渡されているというエラーメッセージ
@user.hoge(1, 2, 3) #=> wrong number of arguments (given 3, expected 2) (ArgumentError)

ところで以下のようなメソッドの場合はどうなるでしょうか。

def hoge(num)
  p num
end

# hoge(1) #=> 1

特定のクラスやモジュール内に定義されていないメソッドの場合、一見レシーバがないように見えます。  
Rubyではクラスやモジュール構文に囲まれていない、一番外側の部分のことをトップレベルと呼びます。  
トップレベルはObjectクラスのインスタンスになります。  
また、Rubyではレシーバを省略した場合、暗黙的にselfがレシーバになります。  
したがってこのメソッドを実行する時は、イメージとして以下のように考えて良いと思います。  

レシーバ メソッド 引数
@object hoge 1(第一引数)

以下のようにすると、メソッドを実行しているオブジェクト名が確認できて、わかりやすいかと思います。  

def hoge(num)
  p self.class
  p num
end

hoge(1) 
#=> Object
#=> 1

テストコードについて

私の環境で実験してみて、パスできたコードを共有致しますので、  
ご参考くださいますと幸いです。  

# app/models/user.rb
class User < ApplicationRecord
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, format: { with: VALID_EMAIL_REGEX }
  validates :email, length: { maximum: 255 }
  validates :name, length: { maximum: 50 }
  validates :name, presence: true
  validates :name, uniqueness: true
end

# test/models/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

  test "should be valid" do
    assert @user.valid?
  end


  test "name should be present" do
    @user.name = " "
    assert_not @user.valid?
  end

  test "email should be present" do
    @user.email = " "
    assert_not @user.valid?
  end

  test "name should not be too long" do
    @user.name = "a" * 51
    assert_not @user.valid?
  end

  test "email should not be too long" do
    @user.email = "a" * 244 + "@example.com"
    assert_not @user.valid?
  end

  test "email validation should accept valid addresses" do
    valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
    first.last@foo.jp alice+bob@baz.cn]
    valid_addresses.each do |valid_address|
      @user.email = valid_address
      assert @user.valid?, "#{valid_address.inspect} should be valid"
    end
  end

  test "email addresses be unique" do
    duplicate_user = @user.dup
    @user.save
    assert_not duplicate_user.valid?
  end
end

環境:  
Mac OS High Sierra 10.13.6
Rails 5.2.1  
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]  

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/16 10:11 編集

    凄くわかりやすい解説をありがとうございます!

    すみませんもう一つ聞きたいのですが、今回質問したエラー文で
    ArgumentError: wrong number of arguments (given 1, expected 2)
    expected 2は何処から来たのでしょうか?

    given 1の方は今回はレシーバが省略されているので、ルール上selfが来て1になったとわかりました。
    ですがexpected の対象のメソッドである
    valid_addresses.each do |valid_address|
    は valid_address の仮引数が1つ定義されてるので
    given 1, expected 1 で返されるのではと悩んでいます。

    それとも今回のexpectedの対象メソッドはeach文では無かったのでしょうか?

    キャンセル

  • 2018/12/16 15:32

    | それとも今回のexpectedの対象メソッドはeach文では無かったのでしょうか?
    はい、そう思います。
    エラーコードに
    ------------------------------------
    Error:
    UserTest#test_email_validation_should_accept_valid_addresses:
    ArgumentError: wrong number of arguments (given 1, expected 2)
    test/models/user_test.rb:41:in test'
    test/models/user_test.rb:41:in block in <class:UserTest>'
    ------------------------------------
    とありますが、
    test "email validation should accept valid addresses" do
    のエラーにもかかわらず、実際にエラーを発生させている箇所が
    test "email addresses be unique" do
    という別のテストになっています。
    これはtest "email validation should accept valid addresses" doのブロックをendでちゃんと閉じていないので、そのテストメソッドの中にtest "email addresses be unique" doが入ってしまっているためだと思います。

    イメージとしては以下のように
    test "aaaa" do
    eachとか色々な処理とかアサーション
    test "bbbb" do
    アサーション
    end
    end

    という風に、test "aaaa"の中にネストする形で別のテストであるtest "bbbb"が入っているような状態だと思います。

    testメソッドは、引数にテスト名とブロックの2つを要求します。
    テスト名はともかく、ブロックがうまく渡せなかったので、
    ArgumentErrorになったのだと思います。

    キャンセル

  • 2018/12/16 15:56

    かなり初歩的なミスが原因だったんですね・・・。
    言われるまで全く気づきませんでした。
    追加の質問まで丁寧に答えて下さり本当にありがとうございました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る