A test case is a set of actions performed on a system to determine if it satisfies software requirements and functions correctly. The purpose of a test case is to determine if different features within a system are performing as expected and to confirm that the system satisfies all related standards, guidelines and customer requirements. The process of writing a test case can also help reveal errors or defects within the system.
Test cases are typically written by members of the quality assurance (QA) team or the testing team and can be used as step-by-step instructions for each system test. Testing begins once the development team has finished a system feature or set of features. A sequence or collection of test cases is called a test suite.
A test case document includes test steps, test data, preconditions and the postconditions that verify requirements. We could see many of these elements present in the use-case examples we looked at already.
When we research test cases, we often find it being applied to systems, often complex systems. While a docstring can include preconditions, even for a basic function as shown below, it should have example test data - what the function receives, and returns - as well.
Even a very basic function in Python that would multiply two arguments should document test cases, specifying what the input is and the expected output.
Test cases define what must be done to test a system, including the steps executed in the system, the input data values that are entered into the system and the results that are expected throughout test case execution. Using test cases allows developers and testers to discover errors that may have occurred during development or defects that were missed during ad hoc tests.
The benefits of an effective test case include:
Guaranteed good test coverage.
Reduced maintenance and software support costs.
Reusable test cases.
Confirmation that the software satisfies end-user requirements.
Improved quality of software and user experience.
Higher quality products lead to more satisfied customers.
More satisfied customers will increase company profits.
Overall, writing and using test cases will lead to business optimization. Clients are more satisfied, customer retention increases, the costs of customer service and fixing products decreases, and more reliable products are produced, which improves the company's reputation and brand image.
Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the code is improved so that the tests pass.
TDD promotes a test-then-code approach. It’s based on the idea that if developers use test cases before writing functionality, they can create better software and, often, complete the project faster. This approach is different from normal software testing, where they generate the code first and only then test it.
TDD developers use test cases before they write a single line of code. This approach encourages them to consider how the software will be used and what design it needs to have to provide the expected usability. Once a test fails, developers understand what needs to be changed and they refactor the code, that is, they rewrite it to improve it without altering its function.
TDD focuses on the code necessary to pass the test, reducing it to essentials. It takes the test unit, or the smallest bit of functionality, as its basis. It’s a bit like building a house brick by brick—no brick is laid down before it’s tested, to make sure it’s sound and that it’s an integral part of the design.
TDD is compatible with the Agile methodology. While Agile focuses on the overall development process, TDD dictates how code gets written. You can combine the benefits of both methodologies to develop high-quality software using test-then-code and short development cycles or sprints.
TDD shifts the focus from implementation to the uses of the software. Through its test-first approach, it gives developers the opportunity to consider more carefully the usability challenges users are likely to face. Software built using the TDD approach is inherently usable—otherwise, it simply wouldn’t pass unit testing.
Tests ensure that the code stays focused and clear during each stage of development. Automated tests, for example, can highlight mistakes and their impact on the system, which in turn enables developers to detect bugs and other problems that arise as a result of the changes they have most recently made to the code. It becomes easier for them to create software that is solid and works as intended.
With TDD, it’s easy to spot functionality issues even as the software is being developed, which allows for these to be more quickly addressed. When you need to build an app that’s fully functional from the first release, TDD can help you ensure your app achieves its purpose.
The codebase gets cleaned up constantly through refactoring, making room for new code to be added. This process can tighten up the code and place it where it belongs in the codebase. The result is a tidy codebase free from unnecessary duplication, which makes the software easier to tweak and maintain.
TDD requires developers to write code in response to test requirements. This approach promotes the simplification of code. It can prevent excess code and helps keep the codebase streamlined and easy to use. There are no doors left open for superfluous code to creep into the software.
Tests create quality metrics for the code. The team of developers can use TDD to effectively determine how good the code is. In this way, they can develop more consistent code and fix any cracks they spot in it.
TDD encourages developers to build software using small units of testable code. This modular approach to software development contributes to creating software that’s more flexible and extensible than that built using other methodologies.
Fast iterations mean that developers can receive feedback from clients more quickly than with traditional approaches to software development. They can then use this feedback to craft a more polished piece of software. Clients also benefit. They can choose to be more involved in the development project and review iterations more frequently.
Other developers can use completed tests as usage examples for the code, allowing them to become familiar with it faster. This can make the project more friendly for developers other than those who build the code from the ground up. For long-term projects which may pass through multiple hands, this documentation can be valuable.
The test-and-code approach increases the productivity of the development team by focusing their energy on passing tests, that is, accomplishing goals. TDD enables you to develop a functional piece of code for end-users in a shorter time span than many other programming practices. This can be a major advantage in today’s competitive software market, where app availability is one of the key factors that determine success.
You can use TDD for both new software and legacy software projects. When applied to legacy software, TDD uses tests to address bugs separately in a way that enables you to resolve them one by one.
One thing to note is that TDD takes one unit of code at a time as its starting point. For very sophisticated software projects, multiple tests may be required to validate each unit of code before writing it. TDD is only as effective as the tests used to move the project into the coding phase. If the testing is not effective or allows bugs to creep in which are then not spotted and resolved, it may undermine the foundation of the software that’s being built.
Use Cases and Test Cases are two important terms in context of Software Testing. A use case tells how a System will behave or perform a certain task provided the given conditions and a test case contains the actual test data, set of instructions to system and expected result of the interaction with system.
Here are the important differences/similarities between Use Case and Test Case- you can see that they are closely related.
You can find a complete tutorial (text and video) on use case testing here.
This website has a few requirements to be met in order to login, including a limited number of attempts at login and a captcha.
Here you can see the use-case representation similar to that for the odoo website- there are a few requirements to facilitate a successful login. Looking at what is needed for a successful login, you can see the site needs to consider what will happen when the "happy day" scenario is not met.
Summary of possibilities
Here are most of the test cases for this use-case mapped into a table- which is a summary and easy enough to read. We could expand the details for each in a full test-case spec. as is done in an example below. We need to test and ensure that the system under test responds appropriately for each. It might seem odd to think that a site might allow login with a bad password, but it can happen if it's not checked for! We often only check that it "does what it is meant to do" (login success) but not that it operates correctly with BAD input.
Each condition where login fails prompts some message from the system. What message?
If the email is incorrect, or not recognised- what does the system do? If the password is wrong- what does the system do? You might be tempted to say "Show a message to say the email is not recognised", or "Tell the user that the password is wrong". That makes sense. It's wrong though, because you would be giving hints to "one with dubious intent" as to correct emails... so for both of these we might just output a message to say "Invalid credentials".
The image of login above shows what happens when the captcha is not completed, and the counter at the top left hints that any more than 3 attempts will likely... what? Block login from this browser for an hour? Melt your face? What might happen?
The result is the expected result. Testing the system with a range of inputs we can see what it does. While we do not want a successful login where an 'X' is shown, we also need to work out (a practical consideration) what actually happens, and what message is fed back to the user. Clearly we would not want the page to crash (500 server error) with bad input.
The use-case table first documents the main parts as outlined in the sample document notes above. I've used green to indicate the normal (basic) flow where everything goes well. For this application, the ATM is a basic simple model like one of the early ones where you just get cash. That's why it assumes that all the customer will do is take out cash.
If it offered a mini-statement, the pre-condition would be that the card is in, the PIN validated, and the customer selected "Withdraw Cash" as the Trigger.
Because the ONLY function is to withdraw cash, the trigger is "insert card". The more features, the more complex the use-case diagram and the more alternate flows may be created.
All exception flows ARE alternative flows. Write them as such, and if you come to a point where the use-case fails, move that case to an exception flow. Not all of the possibilities are even documented here but it gives you a good idea of what might typically happen.
Again, using a colour to add some meaning, orange just means a small wait as the use case can continue and the use-case can still complete.
With the (light) red you can see that the no money comes out and the card may even be swallowed.
We can use a table as shown here to create the test-cases. This one shows a straightforward "nothing goes wrong" scenario.
Note that the centre column identifies user actions, and the right hand column identifies the system (ATM) actions. Note also that for any particular user action, the system might do 1, 2 or even more actions.
In the first case, all went well. You would imagine that the APM should not grant access with a bad PIN. This is another test. Here you can see the extent of the test- not a lot to do, but important to make a note that the number of attempts is important.
This really gives us an idea of another test we can do. Will the machine swallow the card after n repeated bad PINs. This would definitely be an exception flow because not only are you left with no cash, but also no card.
Once you see a problem with the amount entered, you might think... deja vue. No cash. Here's what typically happens when you try to take out €200 when you only have €40 in the account.
Note as always, the centre column has what the customer does, and the right has what the machine does. This may prompt a thought - what if I key in €27? Well... that's actually another test case. Similar steps to this one... but it is another test.