Search code examples
ruby-on-railsregexrailstutorial.org

Using assert_match with regex in Rails unit tests


I'm working on Rails Tutorial Chapter 11 ex 2

The tutorial asks to complete the test by replacing the FILL_IN variables to check for the number of microposts (which could be any number)

  test "micropost sidebar count" do
    log_in_as(@user)
    get root_path
    assert_match "#{FILL_IN} microposts", response.body
    # User with zero microposts
    other_user = users(:mallory)
    log_in_as(other_user)
    get root_path
    assert_match "0 microposts", response.body
    other_user.microposts.create!(content: "A micropost")
    get root_path
    assert_match FILL_IN, response.body
  end

In the first FILL_IN I added in a regex for any number /^[1-9]\d*$/ and tries to interpolating it by assigning it to a variable as follows

regex_number = /^[1-9]\d*$/
assert_match "#{regex_number } microposts", response.body

This is not working for me (there are 34 microposts in the response body for my fixtures)

I checked the regex using a tool and there should be a match for 34

Last thing, how do you check a match for plurals like micropost(s) This is because the final FILL_IN is for "1 micropost"


Solution

  • You shouldn't use regex in this exercise. But let's take this step by step:

    First of all, I don't think regex will work in quotes. After interpolation, your code reads assert_match "/^[1-9]\d*$/ microposts", response.body. For assert_match, you either want a regex OR a string to match, not both. You should drop the interpolation, remove the quotes, and put 'microposts' inside the regex.

    Secondly, I think your regex is broken for this context. '^' matches the beginning of a string, and '$' matches the end of a string, but the assert_match is comparing your regex to only one string: the entire body of the home page! You don't really care where the match is, as long as it exists somewhere in the string. This makes the start and end anchors inappropriate for that search.

    Here's a version of your regex that passes this test:

    assert_match(/[1-9]\d microposts/, response.body)
    

    But this is far too generic for what this test is intended to accomplish. You don't want to look for just any number on the page. You want to look for the correct number of microposts displaying just before the word 'microposts'.

    Looking around the web, this is how most people are completing the FILL_INs for this exercise:

    assert_match "#{@user.microposts.count} microposts", response.body
    

    and

    assert_match "#{other_user.microposts.count} micropost", response.body
    

    Some even simply filled in the number they expect the test to find rather than looking at the User object for the number, but I think that's too easy to break, and probably not what Hartl intended.


    As for Pluralize... I pounded my head against this problem for quite a while. After much agony, I finally found two ways to do it. And I honestly don't think Hartl intended us to use it in this test, because he never used it in a test before. I think he intended for us to directly put the correct pluralization in the search strings as demonstrated above.

    Anyway, here are two ways to complete the second FILL_IN with a pluralization method if you really want to do it this way:

    assert_match "#{other_user.microposts.count} micropost".pluralize(other_user.microposts.count), response.body
    

    or

    assert_match ActionController::Base.helpers.pluralize(other_user.microposts.count, "micropost"), response.body
    

    In practice, you might want to improve readability by doing something like this:

    num = other_user.microposts.count
    assert_match "#{num} micropost".pluralize(num), response.body