10 Tips for Designing Better Test Cases
|Read this meme backwords!
Know your Domain - Understand the System Under Test
We need to deeply understand the system under test, so our tests can bring concrete value, therefore learn the business logic and gain a clear understanding of the requirements - by analyzing the user stories (or what ever specification documents we have available) having knowledge transfers sessions with the domain experts, practice pair testing with more experienced testers from the project, etc. Personally, I'm not advocating automating the manual test cases, as this leads to duplicate work, instead automate to bring the most value, as test automation is demanding and expensive.
Have a Single Point of Failure
A test should ideally be checking one thing only, if at all possible (for UI tests this can’t be avoided for more complex scenarios - based on the real-world customer behavior). This applies to both manual tests and automated checks. Tests that only check one thing are more understandable, easier to diagnose/debug, and are more useful in reporting understandable defects - test steps are used as steps to reproduce in the bug report ticket.
Favor Shorter Tests Whenever Possible
It might initially require a bit more effort, but breaking long/complex test cases into many smaller ones will make our tests easier to understand and both faster and easier to execute. Non-testers will also have an easier time understanding why a particular test is failing when reproducing a related defect. If you're familiar with coding, you can make a parallel with the Unix philosophy - a single function should only do one thing - just as a single test should validate a single part of the functionality, making it easier to maintain and also execute.
Leveraging Test Preconditions
With smart use of test precondition, we can avoid the need to add redundant steps to your tests - some of the typical preconditions would be: logged in, is registered, level of privilege (is the user admin for example), access to a certain environment, etc. Bassically preconditions tell us what we need in order to be able to execute a certain test.
Reduce Repetition with Test Parameters
Test Data Parametrization can help when we are dealing with a lot of input combinations in our test. For example: instead of writing a separate test case for each drop-down option, we can parametrize all the available options and have just one (parametrized) test case - saves us time and improves test readability. This can make our test data-diven, less repeatable and more relevant. Some test-managment tools, like Xray, have built-in support for test parametrization. While others can be edited to use test-parameters using custom fields - I've done this with Testrail. Test parameters are extremelly useful in automation as well, and lot of tools support this, like TestNG, NUnit, JUnit, etx.
Use Shared Steps to Make Future Maintenance Easier
If you notice that two or more tests are using the same steps, turn those test steps into Shared Steps, these can be reused by multiple tests, improving the readability and maintainability of tests and improving the speed of test creation in the long run. If a step that is shared changes you will only need to update it in one place - instead of having to update multiple test cases.
Use Consistent Naming Conventions
This can depend on the project, however, common sense advice would be to make your test names succinct and as descriptive as possible - just by reading the title the purpose of the test should be clear. Just by reading a title of a test case it should be evident what is the purpose behind it.
Group your Tests Logically
Tests Suites (or test sets) can help us here, if you are validating a user story, the test case(s) you create for it should go into one test suite related to that user story, we can link them to each other in Jira, for example. Tests can also be grouped by test runs, by functionality - usually used in regression testing, smoke and sanity tests, etc.
Actual and Expected Results
When we are designing our test cases, we usually add expected result(s) on a test case basis, or for each test step, if these assumptions prove true the test passes. Once we execute the test case(s), we add the actual results as well, if these are different from the expected results we will generally report a defect for a failing test, after investigating why it failed. These should be clear and unambiguous, basically booleans - true or false.
Include Execution Evidence
Not always mandatory, but very nice to have, adding screenshots, or video recordings, of test execution, can provide proof of successful test execution to the stakeholders, or, even more importantly, evidence is even more crucial for failing tests, as it makes reproducing the issue much easier. Many test managment solutions allow us to report bugs direcly from test case execution, saving us some time - this way you have instant steps to reproduce a bug and evidence of the bug - screenshot, recordings, logs, etc.