The Importance of Testing
The value of testing
"If it's worth building, it's worth testing. If it;s not worth testing, why are you wasting your time to workng on it?"
-- Scott Ambler, agiledate.org
Design principles for Apollo
-
Use a High-Level Language: Employing a higher-level language simplified calculations and reduced errors.
-
Divide into Jobs: Breaking down software into smaller tasks optimized memory usage, essential for systems with limited memory like the Apollo computer.
-
Restart on Failure: Instantly restarting a failed job ensured continuous operation, a practice still followed in modern systems like Kubernetes.
-
Checkpoint Good State: Saving and resuming from a known state minimized redundant calculations, a concept echoed in modern stateless container designs.
-
Hardware Monitors the Software: Hardware oversight prevented system hang-ups, a principle akin to preemptive multitasking in contemporary systems.
-
Send Telemetry: Continuous telemetry data provided real-time insights into system health, crucial for decision-making, a practice still vital in modern software monitoring.
You can only test what you know
Testing is essential for ensuring the functionality and reliability of software systems. The principles derived from historical examples like the Apollo Guidance System highlight the importance of testing in ensuring robustness and resilience to failure. Remember, you can only test what you know, emphasizing the necessity of comprehensive testing strategies.
Why Developers Don’t Test
-
I already know it works: Developers may skip testing because they believe their code is functioning correctly. However, overlooking tests can lead to future issues when others work on the code, as they may inadvertently introduce bugs without proper tests to verify functionality.
-
I don't write broken code!: Some developers may argue that they don't need to test because they write code without errors. However, the software ecosystem is dynamic, with constant updates and patches. Without tests, it's challenging to ensure compatibility with new libraries or patches, potentially leading to vulnerabilities or system failures.
-
I have no time: Time constraints are often cited as a reason for skipping testing. However, investing time in writing tests upfront can save considerable debugging time in the future. Tests provide a safety net for refactoring code and adding new features, boosting productivity in the long run.
Why do we need to test?
Testing is crucial because:
-
Prevents future code breaks and compatibility issues: Tests help ensure that changes to code, libraries, or dependencies do not introduce unexpected bugs or system failures, maintaining stability and reliability.
-
Reduces overall development time: While writing tests may seem time-consuming initially, they ultimately streamline the development process by catching errors early and providing confidence for future modifications or additions.
-
Ensures that your code behaves as expected when others use it: In an open-source or collaborative environment, code may be reused in various contexts. Testing ensures that the code behaves predictably regardless of its application, fostering trust among developers and users alike.
Testing Levels and Release Cycle
Software Testing Levels:
-
Unit tests:
- Test individual units or components of a software system.
- Verify that each unit performs as designed.
- Focus on both "happy" paths (expected behavior) and "sad" paths (error handling).
- Typically executed in a development environment or during continuous integration (CI).
-
Integration Tests:
- Combine individual units and test them as a group.
- Expose flaws in the interaction between integrated units.
- Evaluate how modules behave together with various inputs.
- Often performed in a development environment or during Behavior Driven Development (BDD).
-
System Tests:
- Test the entire software process as a complete, integrated system.
- Evaluate the system's compliance with specific requirements.
- Verify that the entire system works together as expected.
- Usually conducted in a staging or pre-production environment.
-
Acceptance Tests:
- Test the system for acceptability based on business requirements.
- Assess whether the system is acceptable for delivery to end users.
- Often performed by end users in an environment similar to or the same as the production environment.
Traditional Release Cycle:
-
Development: Developers perform unit testing and store code in a source code management (SCM) system like Git.
-
Build: Code artifacts are compiled, and further unit testing may be conducted in a build environment.
-
Package Repo: Built artifacts such as Java jar files, Python wheels, or Docker images are stored in a package repository.
-
Test, Stage, and Prod Phases:
- Test: Build artifacts are deployed into testing environments for integration, performance, and compliance testing.
- Stage: Testing environments become more like production, allowing for system testing and acceptance testing.
- Prod: The final production environment where the fully tested and accepted software is deployed for end-user access.
Key Takeaways:
-
The software testing process includes four levels: Unit, Integration, System, and Acceptance.
-
Testing occurs at different levels throughout the phases of the traditional release cycle, starting from development and progressing through testing, staging, and production environments.
Basic of Test Driven Development
Benefits of Test Driven Development
Define test driven development
- means that your unit test cases driver the design
- Your write the tests first for the code you wish you had then you write the code to make the tests pass
- This keeps you focus the purpose of the code
Basic TDD workflow
Red(write a test case that fails)--> Gree(Write code to make it pass-->Refactor(Improve code quality
Why is TDD important for DevOps?
It save time when developing
It allow you to code faster and with more cofidence
It ensures that the code is working as expected
It ensures that future changes don't break your code
In order to create a DevOps CI/CD pipeline, all testing must be automated
Tools for TDD
Automated testing
Frees developers from manual testing
Changes how developers write tests
Popular testing frameworks
- xUnit series
- JUnit for Java
- PyUnit for Python
- NUnit for .Net
- Embunit for C/C++
- Jasomine for Javascript
- Mocha for Node.js
- SimpleTest for PHP
Popular Python testing frameworks
-
PyUnit:
- PyUnit is a unit testing framework for Python, inspired by JUnit. It provides a way to create test cases, test suites, and run them to verify that your code behaves as expected.
- It's a part of the Python Standard Library (
unittest
module) and is suitable for writing and executing unit tests for Python code.
-
Pytest:
- Pytest is a popular testing framework for Python that simplifies writing and executing tests. It offers a more concise syntax compared to PyUnit.
- Pytest supports fixtures, parameterization, and assertions, making it flexible and powerful for various testing scenarios.
- It's widely used for testing Python applications and libraries due to its simplicity and extensibility.
-
Doctest:
- Doctest is a module in Python's standard library that allows you to test Python code embedded in docstrings.
- It's particularly useful for writing tests directly in the documentation, ensuring that code examples in documentation remain accurate and up-to-date.
-
RSpec:
- RSpec is actually not a Python testing framework but a popular testing framework for Ruby. It's used for behavior-driven development (BDD) and is analogous to Python's
unittest
or Pytest. - RSpec allows developers to write tests that focus on the expected behavior of their code in natural language constructs.
- RSpec is actually not a Python testing framework but a popular testing framework for Ruby. It's used for behavior-driven development (BDD) and is analogous to Python's
-
Other Python testing tools
-
Nose:
- Nose is a test runner for Python, extending the capabilities of Python's built-in
unittest
module. It provides a more convenient way to organize and execute tests, especially for larger test suites. - Nose supports test discovery, parallel test execution, and plugins, making it flexible for various testing needs.
- Nose is a test runner for Python, extending the capabilities of Python's built-in
-
Coverage:
- Coverage is a Python tool that measures code coverage during testing. It helps developers identify which parts of their codebase are being tested and which are not.
- By generating coverage reports, developers can ensure their tests are thorough and improve the quality of their code.
Running Tests with Nose
unittest
`python -m unittest discover`
Nose + Pinocchhio
nosetest