Introduction To Testing
Throughout the testing chapter, we will focus on pytest as testing framework, but the concepts described herein apply generally.
Why Should We Test?
Software testing is a critical practice that ensures code works as intended and meets user needs. It provides confidence that code changes and new features don’t introduce unwanted side effects, allowing developers to verify each part of the codebase and catch issues early in the development process.
A comprehensive test suite acts as a safety net, allowing developers to confidently make changes and quickly identify any regressions or unintended behaviors (as, for example, when refactoring code).
Testing also brings clarity to requirements and helps shape different scenarios and edge cases through practices like Test-Driven Development (TDD). This approach often leads to more modular, flexible, and maintainable code. In TDD, the developer writes a test before the code that’s being tested, and then writes the code that satisfies the test.
Testing thus represents not only a way to find bugs, but an integral part of the software development lifecycle that contributes to product quality, reliability, and long-term maintainability, and ensures software meets both user expectations and industry standards.
Types of Tests
Testing is a core aspect of software development, ensuring that code functions correctly and meets its intended requirements. It encompasses various areas, including unit testing, integration testing, system testing, and non-functional testing. By implementing comprehensive testing strategies, developers can identify and resolve issues early in the development process, enhance code quality, and maintain the reliability and scalability of applications.
In the context of this guides, we focus on the core aspect of testing (and writing) code in a modular way, thereby covering the following types of functional tests:
Unit tests: Check that individual functions of the application work correctly and independently. They are usually written using tools like pytest or
unittest
.Integration tests: Check that different functions and more broadly parts of the application work together as expected. They can help identify compatibility, dependency, or communication issues. Integration tests can be written using tools like pytest or
unittest
.
The “testing examples” section will elaborate more on and provide examples of both unit and integration tests.
Although not covered in this chapter, another category of tests known as non-functional exists. As the name suggests, non-functional tests address the non-functional requirements of a system. Non-functional requirements define how a system is supposed to be, in contrast to functional ones, which define what a system is supposed to do. Examples of common non-functional tests are performance tests, security tests and compliance tests, which we briefly present below.
- Performance tests: These tests measure how well the application performs under different conditions, such as load, stress, or concurrency. They can help identify bottlenecks, errors, or resource consumption issues. Performance tests can be written using tools like Locust or
pyperf
. Different types of performance tests address different aspects of performance, such as- Load tests: They aim at understanding the behaviour of a system under a certain amount of e.g. concurrent users performing transactions in a certain timeframe.
- Stress tests: They aim at identifying the capacity limitations of a system under loads that exceed the expected maximum.
- Security tests: These tests check that the application is secure and does not expose any vulnerabilities or risks. They can help prevent unauthorized access, data breaches, or malicious attacks. Security tests can be written using tools like Bandit.
- Compliance tests: Also known as conformance tests, they check that the application complies with certain standards or regulations.