Search code examples
ruby-on-railsjsonrubyassertminitest

Ruby on Rails - minitest assert_select test passes with any parsed JSON, even when it should fail


I can't seem to make my test fail with phony data, so that tells me I'm not properly testing my actual data. My view displays a parsed JSON response (which I've verified in the browser), but I can't seem to verify this in a test.

# Arrange
account = accounts(:good_account) # from fixture
expected_data = { "status": "SUCCESS" }.to_json
phony_data = { "bestBearType": "BLACK" }.to_json

# Act
get some_show_url(account.id)

# Assert
assert_select 'p', JSON.parse(expected_data) # passes
assert_select 'p', JSON.parse(phony_data) # passes (shouldn't)
assert_select 'p', { "bestBearType": "BLACK" } # passes (shouldn't)
assert_select 'p', expected_data # fails

I'm really scratching my head as to how the phony data could be passing. It's almost as if I just have assert_select 'p' without the expected value that follows. Is there an issue using parsed JSON here?


Solution

  • Is there an issue using parsed JSON here?

    You're misusing assert_select, which has nothing to do with JSON, it's for selecting HTML elements from your server's output. I can't tell what your intent is here, but you can't query parsed (or unparsed) JSON to assert_select, or send JSON as any of its arguments.

    In each of your four cases, the behavior of assert_select is as expected:

    • assert_select 'p', JSON.parse(expected_data) # passes

      You passed it an unknown option, "status". That's not an option supported by assert_select, so in effect you're running assert_select 'p', which passes because it finds a <p>.

      When given a hash as its second argument, the options it understands are:

      :text - Narrow the selection to elements that have this text value (string or regexp).

      :html - Narrow the selection to elements that have this HTML content (string or regexp).

      :count - Assertion is true if the number of selected elements is equal to this value.

      :minimum - Assertion is true if the number of selected elements is at least this value.

      :maximum - Assertion is true if the number of selected elements is at most this value.

    • assert_select 'p', JSON.parse(phony_data) # passes (shouldn't)

      Yes, this should pass, for the same reason: You're passing a random unknown option that is ignored, and a <p> is found to match.

    • assert_select 'p', { "bestBearType": "BLACK" } # passes (shouldn't)

      As above, yes, this should pass, "bestBearType" is not a valid option for assert_select.

    • assert_select 'p', expected_data # fails

      This should fail, because the second argument you're giving it is a string, which matches as follows:

      Assertion is true if the text value of at least one element matches the string or regular expression.

      There is no <p> tag in your output that matches the string "{\"status\": \"SUCCESS\"}".

    See the documentation for a complete description and some useful examples.