Best practices always have a purpose, a reason behind them. It's always a good idea to consider these reasons in your design - especially when you are trying to decide how and how hard to follow these best practices.
In this case, the main reasoning behind making every test test a single thing is that if the first thing fails, the second one won't be tested. Since too many opinion makers seem to find merit in breaking everything to the smallest bits possible and wrapping every bit in as much bloat as possible, this gave birth to the idea that every test should contain a single assert.
Don't follow this blindly. Even if every test should test one thing, you should still put some thought into deciding how big or small each "thing" should be, and to do so you must keep in mind why you want every test to test one thing - to make sure a bug in the first thing is not leaving the second thing untested.
So, you need to ask yourself - "do I really need this guarantee here?"
Let's say there is a bug in the first test case - the HTTP response code is not 200
. So you start hacking at the code, figure out why you did not get the response code you should have, and fix the problem. And now what?
- If you manually run the test again, to verify that your fix did solve the problem, you should run into any other problem hidden by the first failure.
- If you don't run it manually(maybe because it takes too long?), and just push your fix waiting for the automated tests server to run everything, then you may want to put different asserts in different tests. The cycles in this case are very long, so it's worthwhile to make the effort to discover as many bugs in each cycle.
There are few more things to consider:
Assertions dependencies
I know the tests you described are just an example, and your actual tests are probably more complicated - so what I'm going to say may not be valid with as much strength in the real tests, but it may still be somewhat effective so you may want to consider it.
If you have a REST service(or any other HTTP protocol) that returns responses in JSON format, you usually write a simple client class that lets you use the REST methods like regular methods that return regular objects. Assuming that client has separate tests to make sure it works, I would have ditched the first 3 asserts and keep only 4!
Why?
- The first assert is redundant - the client class should throw an exception if the HTTP response code is not 200.
- The second assert is redundant - if the response is empty, the result object will be null or some other representation of an empty object, and you won't have anywhere to put key X.
- The third assert is redundant - if the JSON is invalid, you'd get an exception when you try to parse it.
So you don't need to run all these tests - just run the fourth test, and if any of the bugs the first three tries to detect happen the test will fail with a proper exception before you even get the actual assert.
How do you want to receive the reports?
Let's say you don't get emails from a test server, but instead the QA department runs the tests and notifies you of failed tests.
Jack from QA knocks at your door. He says that the first test method failed, and the REST method returned a bad response code. You thank him, and start looking for the root cause.
Then comes Jen from QA, and say that the third test method failed - the REST method did not return a valid JSON in the response body. You tell her that you are already looking at that method, and you believe the same thing that caused it to return a bad exit code also caused it to return something that is not a valid JSON, and looks more like an exception stack trace.
You get back to working, but then Jim from QA arrives, saying that the fourth test method failed and there is no X key in the response...
You can't even look for the reason because it's hard to look at code when you don't have a computer screen. If Jim was quick enough he could have dodged in time...
Emails from the test server are easier to dismiss, but still - wouldn't you rather to just be notified ONCE that something is wrong with the test method, and look at the relevant test logs yourself?