3
\$\begingroup\$
require_relative '../price_entry_class.rb'
require_relative '../exceptions.rb'
require 'spec_helper'

describe PriceEntry do
    it "throws expection on invalid date format" do
        expect { described_class.date_from_input_to_storage("20010101") }.to raise_error(InvalidInputData)
        expect { described_class.date_from_input_to_storage("zażółć") }.to raise_error(InvalidInputData)
        expect { described_class.date_from_input_to_storage(nil) }.to raise_error(InvalidInputData)
        expect { described_class.date_from_input_to_storage(-19_199) }.to raise_error(InvalidInputData)
    end
end

Is it OK to have multiple expectations in a single Rspec test? rubocop-rspec claims that it would be preferable to separate this test into 4:

spec/price_entry_class_spec.rb:14:2: C: Example has too many expectations [4/1].

Claim that it is always better to have single assert in every unit test is quite surprising for me. Is there a good reason to split test like this into four separate ones?

This test is testing only one thing at a time (is obviously malformed input resulting in exceptions).

I can imagine rewriting it to

["20010101", "zażółć", nil, -19_199].each do |malformed_input|
    expect { described_class.date_from_input_to_storage(malformed_input) }.to raise_error(InvalidInputData)
end

but I am not convinced that it is improving readability, maintainability or anything else.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

The reason is that if this test fails, you'll have to separately determine which of the 4 sub-expectations has failed.

Depending on the output of your test reporter, this may or may not be a practical problem.

This test is testing only one thing at a time (is obviously malformed input resulting in exceptions).

Actually, you're testing:

  • invalid digits raise an error
  • non-date characters raise an error
  • nil raises an error
  • negative numbers raise an error

That said, if you're essentially doing manual fuzzing and don't care about those distinctions -- though I'd say they seem useful in this particular case -- then I don't see an issue with this.

Testing rules and best practices are helpful, but don't get too bogged down in them. If you know your own testing goals, and understand why it might be ok to depart from a best practice, by all means do so.

\$\endgroup\$
3
  • \$\begingroup\$ "you'll have to separately determine which of the 4 sub-expectations has failed" - not in rspec, it reports line where assert failed. The only problem that I see is that after failure further assert in the given test will not be run. \$\endgroup\$ Commented Jul 14, 2017 at 19:05
  • 1
    \$\begingroup\$ As I said, it may not be a practical problem. That said, semantically it's cheating. You're relying not only on "This test case failed" but also on the specific error report to determine where the failure was. I think it's a pedantic distinction in this case, though. \$\endgroup\$
    – Jonah
    Commented Jul 14, 2017 at 19:09
  • \$\begingroup\$ One final thought, if you did want to make your intention clear, you could write a "fuzz" helper, to which you'd provide "a bunch of random bad data" (if that's how you're thinking about it), and have it run each piece of data through a provided function (date creation in this case). If you really don't care about the distinctions among the sample data, a test result of "some random data caused an error" should be enough for you. If you do care about the distinctions, you should probably have multiple test cases. \$\endgroup\$
    – Jonah
    Commented Jul 14, 2017 at 19:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.