As other answerers have mentioned, there are situations where using multiple assertions adds transparency and value to your test cases and there are situations where a single assertion is the best choice to keep tests fast, independent/isolated, repeatable and self-validating. Test cases must also thoroughly validate the results of an action which is executed, and if it requires multiple assertions to validate the correctness of the code, then so be it.
One scenario where multiple assertions are quite valuable is for verifying API tests. API testing is a great example because we typically perform one action which we are testing, a single API call, but the response from that API call will more than likely contain a complex object with many strings, numbers, booleans, arrays, and other objects. If a single action modifies more than one property in the system, then each property must be validated in order to determine that the code is correct and that the system is in the expected state.
Some test frameworks, such as NUnit for C# and Jasmine for JavaScript, use soft assertions and will execute every assertion in the test case, and then at the end, report on every detected failed assertion. Jasmine is able to pull this off because their test runner and assertion library are tightly integrated.
But many other testing frameworks use hard assertions. They throw errors or exceptions at the first encountered failing assertion. This reduces transparency by hiding other possible defects in the system.
One argument against multiple assertions is that an early failure masks other failures in the system, requiring developers and testers to apply a fix and rerun the test to further detect errors hidden by the previously failed assertions. In some cases this may be good. Some may prefer this more iterative methodology. Others may prefer the advantages conferred by having full transparency. Perhaps seeing all of the reported failed state in the test case may help developers and testers analyse the defect and apply the best possible fix.
I recently discovered Jasmine executes all assertions, while Mocha and Chai together do not. To avoid the costs of switching to Jasmine, I built a Node.js module called multi-assert, which wraps groups of assertions together in order to execute each one and report all the failures in an easy-to-read format, such as in the example below:
1) Test - async/promises
should expect status to be yellowblue, yellowred, bluegreen, and 3 to equal 4:
AssertionError:
MultipleAssertionError: expected 3 to equal 4
at /Users/user123/proj/multi-assert/examples/example-async.spec.js:17:32
at /Users/user123/proj/multi-assert/multi-assert-async.js:12:27
at Array.map (<anonymous>)
MultipleAssertionError: expected 'bluegreen' to equal 'yellowblue'
at /Users/user123/proj/multi-assert/examples/example-async.spec.js:16:75
at async /Users/user123/proj/multi-assert/multi-assert-async.js:12:21
at async Promise.all (index 0)
MultipleAssertionError: expected 'bluegreen' to equal 'yellowred'
at /Users/user123/proj/multi-assert/examples/example-async.spec.js:19:75
at async /Users/user123/proj/multi-assert/multi-assert-async.js:12:21
at async Promise.all (index 3)
at /Users/user123/proj/multi-assert/multi-assert-async.js:22:23``
So, what problem does this -- the ability to enable soft assertions -- solve? It eliminates one of the reasons to not use multiple assertions. It nullifies the idea that multiple assertions are bad because they hide information.
But does this mean you should use multiple assertions everywhere? As others have mentioned, multiple assertions may be a sign that a test case is actually performing multiple, unrelated actions within a single test case. Before using multiple assertions, take a good look at your test case and ask yourself if the test case can be split up into separate test cases.
After splitting apart one of these test cases, if you find part of the derived test cases contain duplicated code, then examine what part specifically is duplicated. If it is the setup portion, consider refactoring the setup portion into a before hook, and group those test cases into a single suite so the test action and assertions are presented in reports as clearly distinct tests. If one fails, it is then much more clear to developers and testers responsible for fixing the defect what has gone wrong.
In conclusion, there is nothing wrong with multiple assertions or single assertions. Instead, what is important is that test cases are written in such a way that they're fast, independent and isolated from one another, that they can be easily repeatable, are self validating, and that they are complete and thorough. How to achieve this depends on critically analyzing the specific nature of what is being tested.
RowTest
(MbUnit) /TestCase
(NUnit) to test a variety of edge-case behaviors. Use the proper tools for the job! (Unfortunately, MSTest doesn't seem to have a row-test capability yet.)RowTest
andTestCase
using test data sources. I'm using a simple CSV file with great success.