12

Right now I have unit tests that are failing using the "Faker" Company Name.

it seems like the expect(response.body).to match(@thing.name) is whats getting messed up.

When looking at the error, the Faker Company names will sometimes have things like "O'Brian Company" or "O'Hare Company" or similar.

Is faker an encoded string? since I know it's not a good idea to match on encoded strings, and I really don't want to just specify a specific company name in the Factory im using.

Thanks

msmith1114
  • 2,717
  • 3
  • 33
  • 84
  • I'm curious why you felt like my answer wasn't sufficient. Anything I can clarify? – Mickey Sheu Jan 30 '17 at 17:23
  • The problem with include is that if it "contains" it, I want it to match it completely and not just that the word "exists" within the name. Although I may use it as a fallback, in which ill mark your answer correct :). Im really just trying to look for other possibilities. – msmith1114 Jan 30 '17 at 17:26

4 Answers4

19

Faker won't do any encoding for you. It will just give you a string like O'Malley. But the response should have HTML escaping (or some other kind, depending on the format), like O'Malley. You could always puts response.body to see for sure.

The RSpec matches matcher is really designed for either expected or actual to be a regular expression, but in your case both are strings. Because the code has an optimization calling values_match? which does a simple comparison, you are effectively saying expect(response.body).to eq(@thing.name).

If you do want a regular expression, you are right that you should be careful using uncontrolled values to create it. Fortunately Ruby has Regexp.escape for that, so you can say Regexp.new("foo" + Regexp.escape(@thing.name) + "bar"). But from your objection to include, it sounds like you actually want the response to contain nothing but the name, right? In that case, you don't need a regex at all.

In any case, the problem isn't about what's around the name, but how the name is escaped. So before comparing you should either (1) decode the response or (2) encode the faker string. It doesn't really matter which. Both are pretty easy:

expect(CGI.unescapeHTML(response.body)).to eq @thing.name

or

expect(response.body).to eq CGI.escapeHTML(@thing.name)

Naturally, if your response is JSON, you should replace all this HTML escaping stuff with JSON, etc.

Paul A Jungwirth
  • 23,504
  • 14
  • 74
  • 93
3

Assuming you are referring to Faker::Company of the Faker gem

The correct way to make your example expectation pass would be to use a Regexp as in the @rafael-costa example. Doing so escapes things like apostrophes.

The problem with using Faker is that your tests are not deterministic. It is best practice to provide static, known inputs for your test and expect the outputs to pass certain expectations based on those inputs. It is difficult to provide a pertinent example without more information but maybe something like this:

company = Company.new(name: 'Acme Anvils')
get :show, params: {id: company.to_param}, session: {}
expect(response.body).to match(Regexp.new('Acme Anvils', Regexp::MULTILINE))

Also you typically should not test specific body output within your controller specs. To do so is testing across purposes. You would normally write a view test for that.

Community
  • 1
  • 1
Midwire
  • 1,090
  • 8
  • 25
2

You could try passing a Regexp instead of the string:

expect(response.body).to match(Regexp.new(@thing.name))

Also, If the problem is only when you get this type of names from faker, then you should take a look at this QA, It gives some good insights.

Community
  • 1
  • 1
Rafael Costa
  • 1,291
  • 2
  • 13
  • 30
  • I guess the issue in NOT using it, is I would need to define a lot of sample test data. But in a way it's good because it does show issues with odd values for example. – msmith1114 Jan 30 '17 at 19:02
1

You could try using #include instead of using #match.

expect(response.body).to include(@thing.name)
Mickey Sheu
  • 758
  • 7
  • 16