How to write test cases might not seem like such an important part of development. But in order for a software tester to best perform their job, they need a crystal clear set of steps to follow and a clear definition of what is being tested.
Everyone from NASA and GE to enterprise-level corporations can benefit from teams operating at their best. Writing excellent test cases is just one more way to enhance team efficiency and efficacy and Parasoft is all about empowering teams to do just that.
In this blog, we cover the following topics related to how to write a test case:
A test case is exactly what it sounds like: a test scenario measuring functionality across a set of actions or conditions to verify the expected result. They apply to any software application, can use manual testing or an automated test, and can make use of test case management tools.
A key thing to remember when it comes to writing test cases is that they are intended to test a basic variable or task such as whether or not a discount code applies to the right product on an e-commerce web page. This allows a software tester more flexibility in how to test code and features.
The difference between test cases vs. test scripts should also be clarified. A test script is a short program meant to test certain functionality. A test case is a document with steps to be completed as planned out ahead of time.
Consider test cases as a meticulously planned trip and test scripts to be more like a quick trip to the grocery store.
Test cases can measure many different aspects of code. The steps involved may also be intended to induce a Fail result as opposed to a positive expected result such as when a user inputs the wrong password on a login screen.
Some common test case examples would be the following:
Test cases can be applied to any number of features found in any given software. Some of the most popular include:
Test cases come in handy in a variety of software scenarios. Everything from banking to personal software requires a test case application. For example, if the goal is to have encrypted, sensitive data, the software needs to have features that work as intended.
But functional testing is just one aspect of writing a test case. Software testing should robustly challenge every aspect of the code from performance to compatibility to security. That’s why personal encryption software needs to be tested so thoroughly — especially when it comes to things like Web APIs.
Writing test cases varies depending on what the test case is measuring or testing. This is also a situation where sharing test assets across dev and test teams can accelerate software testing. But it all starts with knowing how to write a test case effectively and efficiently.
Test cases have a few integral parts that should always be present in fields. However, every test case can be broken down into 8 basic steps.
Step 1: Test Case ID
Test cases should all bear unique IDs to represent them. In most cases, following a convention for this naming ID helps with organization, clarity, and understanding.
Step 2: Test Description
This description should detail what unit, feature, or function is being tested or what is being verified.
Step 3: Assumptions and Pre-Conditions
This entails any conditions to be met before test case execution. One example would be requiring a valid Outlook account for a login.
Step 4: Test Data
This relates to the variables and their values in the test case. In the example of an email login, it would be the username and password for the account.
Step 5: Steps to be Executed
These should be easily repeatable steps as executed from the end user’s perspective. For instance, a test case for logging into an email server might include these steps:
Step 6: Expected Result
This indicates the result expected after the test case step execution. Upon entering the right login information, the expected result would be a successful login.
Step 7: Actual Result and Post-Conditions
As compared to the expected result, we can determine the status of the test case. In the case of the email login, the user would either be successfully logged in or not. The post-condition is what happens as a result of the step execution such as being redirected to the email inbox.
Step 8: Pass/Fail
Determining the pass/fail status depends on how the expected result and the actual result compare to each other.
Same result = Pass
Different results = Fail
Each part of a well-written unit test will define several core aspects including:
It’s important to know that the standard format of well-written tests is composed of the following parts:
As mentioned, there is a standard test case format. However, the test case template would likely vary from company to company and even from team to team. Instead, a test case template is the document with a list of test scenarios and subsequent test cases.
Though test cases will vary based on the type of testing and overall field of testing, building a quality test case comes down to those few reliable items above. Remember: the name of the test method must include the method or unit under test and what is the expected outcome.
It should also be noted that each unit should be tested in isolation. In this case, “isolation” means keeping tests focused as much as possible in order to execute only the piece of the application we are testing for.
This example comes from a banking-related test case:
With this method name, we know that this is a unit test that is:
A meaningful method name allows anyone reviewing the results to understand what the unit test was testing for. Moreover, it signals the data to be tested, the expected result, and what was tested.
If the test fails, knowing the expected result is critical in allowing for easier troubleshooting and ensuring no regressions are introduced.
The data used needs to be enough to execute the test. For unit testing, we want to make it as simple as possible to test the most basic unit of our application. The data could be as simple as making a string or object variable for which you can control the data. Or a mock framework can be used for the test if a dependency is not available or you need that dependency to be in a specific state.
Having just enough to test that one part if sufficient. You DO NOT need to configure every piece of the application for the test to run.
All of this affects how the unit test will behave since this is the data being used for unit test execution. As such, this part of unit testing is the most time consuming as it requires some understanding of the code you are testing to know what data to use for testing.
Keep it simple by using just the parts needed for the code being tested. Mocks are very useful in this phase as they allow you to control how methods from those objects will behave when interacting with your test.
For example, given the following data:
We avoided the “real customer class” by using a mock for the “customer class” for testing isolation. We do not want to introduce nor configure another object for this test as it adds another layer of maintainability for that object, and it is not affecting the result of the method under test.
The next variable to be created is the “initial balance”—something known due to knowledge of the code. The next line shows the Account object being created along with the mock and the Initial Balance to prepare the method we are testing for with the data we just used.
So in this example, the account object is configured with the mock customer since we do not care about the customer object’s data and we passed an initial balance that we can control for our test.
The next line defines the input as the method under test requires a number to be used. We defined the “balance” to be used in the method we are testing for. Then the method is executed with the result of the method being stored in our variable for us to use later.
Once the test can complete successfully (as in it runs from start to finish with no exceptions or errors), then it is time to apply an assertion to the unit test. Without the assertion, the unit test is meaningless since there is nothing you are enforcing to ensure it is working as intended.
Collecting coverage of what lines were executed does tell what was executed but it does not provide enough detail to determine the following:
An assertion can be as basic as:
As long as the unit test contains one assertion that is checking the method under test result, this is a meaningful unit test.
By applying the standard format of unit test, a team can easily maintain, read, and/or update tests with more ease to readily see where more testing can be applied to the rest of the application.
How to write effective tests and test cases can be streamlined over time. Some best practices include using strong titles, strong descriptions, and keeping the language concise and clear.
But you’ll also want to include preconditions, assumptions, and the expected results, too. All of this information is relevant for the software tester — especially when determining whether the test case should be a “pass” or a “fail” instead.
A cheat sheet for creating test cases that work well is as follows:
Simple, unique, specific, open to feedback, and focused on reusability: that’s the way of a great test case. For a more visual look at how to write a quality test case, check out Parasoft’s webinar on the subject.
The other aspect of a test case involves test suites and test plans. These differ in key ways and both are vital to accurate test case development.
A test suite comes into play for test cases as it relates to source code, the collection of dependencies, or the suite of tests to be performed on code. Test suites allow you to categorize test cases in ways that align with any analysis or planning needs.
This means that core software features might have their own test suite while another test suite is for a specific testing type such as smoke or security. Think of test suites as a bookshelf to organize your test cases on.
In contrast, a test plan is more like the umbrella that stands over all of the test suites. If test cases are books and test suites are bookshelves, then test plans are the room that contains the bookshelf.
Generally, test plans are set up in terms of manual tests, automated tests, and a general format of how to go about testing. They’ll test the software from the foundation up utilizing test suites and test cases before implementing changes or adding new features.
Parasoft generally develops its tools and suites with the “George Jetson” theory in mind. That is to say that we want our clients to be able to “press a button” and have everything taken care of. While this isn’t totally realistic, tools that have this focus on automation are the best to use when it comes to writing test cases.
Not only can they assist with automation, but they can help from the very beginning of development. After all, it’s too easy to get bogged down by small details or features. One might forget that software just has to function first. That’s where a Java unit testing tool like Parasoft Jtest comes in.
This tool allows beginners and experts alike to improve their unit testing skills more quickly, as well as the unit testing experience. After establishing a foundation, it executes the unit tests then guides the user to ensure that the tests are meaningful. When you can understand the kinds of things to look for in a test, test case writing becomes less intimidating.
As a Solution Architect at Parasoft, William helps teams strategize and prioritize as they adopt modern software development and testing practices into their organization.