39

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

6 Answers 6

52

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]
Sign up to request clarification or add additional context in comments.

4 Comments

Hi. Cool answer! +1. I have several questions, maybe too long for comments. Would you mind checking chat.stackoverflow.com/rooms/28565 ?
Hi, sorry i couldn't make it. Do you still have questions?
yeah, can you take a look at the link in my comment?
Doesn't work on TestUnit 3.3 w/ Ruby 2.7: "NoMethodError: undefined method `message' "
12

There is assert_includes:

assert_includes 'foobar', 'foo'

will assert that foobar contains foo.

3 Comments

The format should be assert_includes result, 'foo' in order to test for the substring 'foo'.
Surely this should be the accepted answer?
@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.
8

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.

4 Comments

is it possible to use assert_match? What pattern should I use?
@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.
Sure this works but the error message on failures Expected false to be truthy. is not very helpful compared to using assert_match.
@Andrew assert takes an optional message that will be printed in case the assertion fails. You can use that to get more informative message
2

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.

Comments

0

Like this:

assert string_to_test.index(substring_to_verify)

The .index method returns nil if the substring is not found, which will cause the assert to fail.

Comments

0

I would use assert_match:

require 'test/unit'

class MyTest < Test::Unit::TestCase
  def test_match
    assert_match( /aa/, 'xxaaxx')
  end
  def test_match_fail
    #~ assert_match( /aa/, 'xxbbxx')  #fails
  end
end

If you need it often, you could extend TestCase:

require 'test/unit'

module Test
  module Unit
    class TestCase
      #Define new assertion
      def assert_contains(string_to_test, substring_to_verify)
        assert_match( string_to_test, substring_to_verify)
      end
      def assert_not_contains(string_to_test, substring_to_verify)
        assert_not_match( string_to_test, substring_to_verify)
      end
    end
  end
end
class MyTest < Test::Unit::TestCase
  def test_contains()
    assert_contains( /aa/, 'xxaaxx')
    assert_contains( 'aa', 'xxaaxx')
  end
  #~ def test_contains_fail()
    #~ assert_contains( 'aa', 'xxxxxx')
    #~ assert_contains( /aa/, 'xxxxxx')
  #~ end
  #~ def test_contains_not_fail()
    #~ assert_not_contains( /aa/, 'xxaaxx')
    #~ assert_not_contains( 'aa', 'xxaaxx')
  #~ end
  def test_contains_not()
    assert_not_contains( 'aa', 'xxxxxx')
    assert_not_contains( /aa/, 'xxxxxx')
  end
  def test_contains_special_characters()
    assert_contains( '[aa', 'xx[aaxx')
    #~ assert_contains( /[aa/, 'xx[aaxx')
  end
end

Remarks:

  • When you use special regexp-characters (like []....) then you can use it in strings (at least my experiment in test_contains_special_characters worked.
  • You can define your own regexps.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.