Automated testing is an important part of industry software engineering development, and is virtually present at all major companies. While in certain companies, this role will be covered by a dedicated QA team, in others, it will be the responsibility of the developers themselves, e.g., at Amazon. Regardless, understanding and being able to write unit tests is important for a SWE to be able to communicate and standby their code.
The automated testing process is part of the wider build/development process and is usually run as part of the CI/CD pipeline. The ultimate goal of automated testing is to ensure that the code functions as expected, and that any changes to the codebase do not break existing functionality.
As background, most companies to implement their CI/CD pipeline with their Git implementation (of which are usually internal), and so we will be talking about unit testing and integration testing from the point of you pushing your code to a Git repository. So imagine that all of these tests will be executed from the moment that you push your code to your project repository.
Further, there are usually "pipelines" for each project (e.g., AWS CodePipeline), and each of your code commits "flows" through your project pipeline. Generally, pipelines are usually composed of a series of "stages" that are executed sequentially. For example, after some cloud machine builds your code, the first testing stage might be to run the unit tests, and the second testing stage might be to run the integration tests. These setups vary from project to project, and you may be able to override passing criteria, but testing can lead credence to your implementation, and can support allow your code to promote (pass on to) production.
Unit tests are meant to test your code commit(s) specifically, rather than dependencies and other modules that may be present in the code base. So unit tests go well with the microservices architecture. The industry will use various frameworks for unit testing, depending on the stack, e.g., language. For example, Python uses unittest, Java uses JUnit or Mockito, and JavaScript/TypeScript uses Jest. A good choice is one that supports mocking, spies, and other features that allow you to avoid testing 3rd party function calls (don't run them). Instead you should assume some expected behaviour from 3rd party function calls, and write tests that cover every "branch" of your code (code coverage). With that stated, some resources for the most important parts of unit testing are:
- Test Coverage:
- Atlassian: What is Test Coverage?
- Microsoft: Code Coverage
- Unit Test Cases:
- Guru99: Unit Testing Tutorial
- AWS Unit Testing: Getting started with testing serverless applications
- Coursera: How to Write Test Cases: A Step-by-Step QA Guide
- Mocking:
- Mocking in Python: Mocking in Python
- Mocking in Java: Mockito Tutorial
- Mocking in JavaScript: A guide to module mocking with Jest
Integration testing, in contrast to unit testing, is meant to verify that your code works with other components/modules/services. We want to test that overall the entire system works as expected. They often execute after setting a production-like environment, and integration test frameworks can differ between projects, but while there aren't really any ubiquitous integration testing specific frameworks, services like CircleCI can help automate the process, and Selenium is a popular tool for testing web applications. Some resources for the most important parts of integration testing approaches are:
- General:
- Big Bang Approach:
- Educative: What is big bang testing?
- Incremental Approach:
- Educative: What is incremental testing?
- Sandwich/Hybrid Approach:
- Educative: What is the hybrid testing approach?