This sometimes occured in our code base when generating
names with the fakedata package, someone named o'conner
randomly fails a particular test.
Also add tests for the other matching function and fixed them.
Furthermore, I snuck in logging of the matches as well.
Even though functions that use this one all have HasCallStack, the fact
that this function itself doesn't means that all errors are reported as
from this line anyway:
Failures:
./Yesod/Test.hs:1571:28:
1) ...
This should correct that.
My team makes frequent use of `statusIs`, but in virtually all cases where `statusIs` fails, we need to add a call to `printBody` to do further debugging.
Following in the footsteps of `requireJSONResponse`, this PR automatically prints a portion of the body when `statusIs` fails, assuming the body looks like a text-based response (e.g. not a JPEG).
I've found that a status code alone is often very misleading and leads people on a wild good chase, because e.g. a 403 could be triggered for many different reasons.
I'm opening this PR as a draft to confirm people like the idea of doing this. If so I'll do a closer review of the code (this is my first draft basically), and also write some tests + test the code works in all cases.
This function checks that a response body is JSON, and parses it into a Haskell value. Having something like this function is pretty essential to using Yesod as a JSON API server, so I think it's a good addition. You can use it to parse a Haskell record directly (usually by adding FromJSON classes to your response types), or parse a Value and pull out individual fields, maybe using something like `aeson-lens` (though probably a testing-specific library would be better).
I debated over these things:
1. The name. I was thinking of something like [assert/require/decode/parse]JSON[Response/Body]. I ultimately went with requireJSONResponse:
- decode/parse sound like the aeson functions that return Either or Maybe, and I wanted this function to throw an error if it failed
- I'm open to using `assertJSONResponse`—it matches the other functions (`assertEq`) better—but I think it reads less like English.
- I chose Response over Body because (a) It also checks the content-type header, which is not in the body (b) "Body" felt slightly in-the-weeds of HTTP; I think "response" is more approachable.
2. Should it require the JSON content type? You can definitely have a server that returns JSON without JSON content types, but I think that's a such a bad idea, it's more likely requiring it helps people if they accidentally don't add the header.
3. Should it take a String parameter to add to the error message? This would match `assertEq`, but other functions like `statusIs` don't take a message. Ultimately I went without it, because the messages felt like I was repeating myself: `(comment :: Comment) <- requireJSONResponse "the response has a comment"`