In Ruby unit tests, how to assert that a string contains certain substring?

24,786

Solution 1

You could go with assert_match pattern, string, [message] which is true if string =~ pattern:

assert_match substring_to_verify, string_to_test

e.g.

assert_match /foo/, "foobar"

If you use this very often, why not write your own assertion?

require 'test/unit'

module Test::Unit::Assertions
  def assert_contains(expected_substring, string, *args)
    assert_match expected_substring, string, *args
  end
end

Alternatively, using the method described by @IvayloStrandjev (way easier to understand), you could define

require 'test/unit'

module Test::Unit::Assertions
  def assert_contains(expected_substring, string, *args)
    assert string.include?(expected_substring), *args
  end
end

The usage is exactly as you requested in your question, e.g.

class TestSimpleNumber < Test::Unit::TestCase
  def test_something
    assert_contains 'foo', 'foobar'
  end

  def test_something_fails
    assert_contains 'x', 'foobar', 'Does not contain x'
  end
end

Which will produce

Run options:

# Running tests:

.F

Finished tests in 0.000815s, 2453.9877 tests/s, 2453.9877 assertions/s.

  1) Failure:
test_something_fails(TestSimpleNumber) [assertion.rb:15]:
Does not contain x

2 tests, 2 assertions, 1 failures, 0 errors, 0 skips

Edit

As requested, with automated message:

module Test::Unit::Assertions
  def assert_contains(exp_substr, obj, msg=nil)
    msg = message(msg) { "Expected #{mu_pp obj} to contain #{mu_pp exp_substr}" }
    assert_respond_to obj, :include?
    assert obj.include?(exp_substr), msg
  end
end

adapted from the original assert_match source. This actually also works with Arrays!

assert_contains 3, [1,2,3]

Solution 2

There is assert_includes:

assert_includes 'foobar', 'foo'

will assert that foobar contains foo.

Solution 3

You can write assert string_to_test.include?(string_to_verify) for instance. You can not expect to have asserts for all the checks you would like to perform, so just go the the classic check of a boolean condition.

Also have a look here to see a list of all available assertions.

Solution 4

I'd use one of these:

assert(string_to_test[substring_to_verify])
assert_equal(substring_to_verify, string_to_test[substring_to_verify])

They accomplish the same thing so the first is my usual choice.

Share:
24,786
Louis Rhys
Author by

Louis Rhys

trying to learn and help others learn :)

Updated on November 01, 2020

Comments

  • Louis Rhys
    Louis Rhys over 3 years

    In a Ruby unit test, how do I assert that a string contains a substring? Something like:

    assert_contains string_to_test, substring_to_verify
    
  • Louis Rhys
    Louis Rhys about 11 years
    is it possible to use assert_match? What pattern should I use?
  • Ivaylo Strandjev
    Ivaylo Strandjev about 11 years
    @LouisRhys I believe so, though I never used it(and it is only now that I notice it). Maybe try: assert_match(".*#{string_to_verify}.*", string_to_test). Should work if I am not wrong.
  • Louis Rhys
    Louis Rhys about 11 years
    Hi. Cool answer! +1. I have several questions, maybe too long for comments. Would you mind checking chat.stackoverflow.com/rooms/28565 ?
  • Patrick Oscity
    Patrick Oscity about 11 years
    Hi, sorry i couldn't make it. Do you still have questions?
  • Louis Rhys
    Louis Rhys about 11 years
    yeah, can you take a look at the link in my comment?
  • Peter Gordon
    Peter Gordon over 5 years
    The format should be assert_includes result, 'foo' in order to test for the substring 'foo'.
  • Andrew
    Andrew over 2 years
    Sure this works but the error message on failures Expected false to be truthy. is not very helpful compared to using assert_match.
  • Ivaylo Strandjev
    Ivaylo Strandjev over 2 years
    @Andrew assert takes an optional message that will be printed in case the assertion fails. You can use that to get more informative message
  • NobodyMan
    NobodyMan over 2 years
    Doesn't work on TestUnit 3.3 w/ Ruby 2.7: "NoMethodError: undefined method `message' "
  • SamCosta1
    SamCosta1 almost 2 years
    Surely this should be the accepted answer?
  • Rorrim
    Rorrim almost 2 years
    @SamCosta1 It works, but it invalidates the paradigm where the left side of the expression is the "expected" result, whereas in this case is the thing we are testing instead.