# Software Testing Basics

Software testing is the process of evaluating and verifying that a software product or application does what it is supposed to do. The benefits of testing include preventing bugs, reducing development costs, and improving performance.

## 1. The Testing Pyramid

A common strategy for structuring tests is the **Testing Pyramid**, which suggests having many unit tests, fewer integration tests, and very few end-to-end tests.

## 2. Unit Testing

Unit testing involves testing individual components or functions of the software in isolation. These are the fastest and cheapest tests to write and run.

*   **Scope:** A single function, method, or class.
*   **Goal:** Validate that each unit of the software performs as designed.
*   **Tools:** Jest (JS), JUnit (Java), PyTest (Python).

### Example (JavaScript/Jest)
```javascript
// function to test
function add(a, b) {
    return a + b;
}

// test
test('adds 1 + 2 to equal 3', () => {
    expect(add(1, 2)).toBe(3);
});
```

## 3. Integration Testing

Integration testing verifies that different modules or services used by your application work well together.

*   **Scope:** Interaction between two or more units (e.g., Database + API, or Service A + Service B).
*   **Goal:** Expose faults in the interaction between integrated units.

### Example Concept
If you have a function that saves a user to a database, an integration test would actually spin up a test database, call the function, and verify the record exists in the database.

## 4. End-to-End (E2E) Testing

E2E testing simulates real user scenarios from start to finish. It tests the application as a whole.

*   **Scope:** The entire application (Frontend + Backend + Database).
*   **Goal:** Validate the system from the end-user's perspective.
*   **Tools:** Cypress, Selenium, Playwright.

### Example Concept
A script opens a web browser, navigates to your login page, types a username and password, clicks "Login", and asserts that the dashboard loads.

## 5. Test Driven Development (TDD)

TDD is a software development process where you write the test *before* you write the code.

1.  **Red:** Write a failing test.
2.  **Green:** Write just enough code to pass the test.
3.  **Refactor:** Improve the code while keeping the test passing.

## 6. Smoke Testing

Smoke testing (also known as "Build Verification Testing") is a preliminary test to reveal simple failures severe enough to, for example, reject a prospective software release.

*   **Origin:** The term comes from hardware testing: "Plug it in and see if smoke comes out."
*   **Goal:** To verify that the critical functionalities of the system are working fine. It does not go into deep detail.
*   **When:** Performed on every new build before more rigorous testing (Integration/E2E) begins.
*   **Example:** Can the application launch? Can I log in? Does the main dashboard load?

## 7. Sanity Testing

Sanity testing is a subset of regression testing performed after receiving a software build with minor changes in code or functionality. Its purpose is to ensure that the bugs have been fixed and no further issues were introduced by these changes.

*   **Focus:** Narrow and deep. It focuses on the specific functionality that was changed.
*   **Smoke vs. Sanity:**
    *   **Smoke:** "General Health Check" (Wide and Shallow). Covers the entire system's critical paths.
    *   **Sanity:** "Specialized Health Check" (Narrow and Deep). Verifies that a specific fix works.
*   **When:** After bug fixes or minor code changes.

## 8. Regression Testing

Regression testing is the practice of running existing tests to ensure that recent code changes (bug fixes, new features) have not broken existing functionality.

*   **Goal:** To catch "regressions" (bugs that reappear or new bugs in old code).
*   **When:** Whenever code is modified.
*   **Automation:** Because regression suites grow large over time, they are prime candidates for automation in CI/CD pipelines.

## 9. Performance Testing

Performance testing determines how a system performs in terms of responsiveness and stability under a particular workload.

*   **Load Testing:** Testing the system under expected normal and peak load conditions.
*   **Stress Testing:** Testing beyond normal operational capacity to find the breaking point.
*   **Scalability Testing:** Determining if the system can scale up (add resources) to handle increased load.
*   **Tools:** JMeter, k6, Gatling.

## 10. Security Testing

Security testing ensures that the software is secure from external threats. It aims to identify potential loopholes and weaknesses that could result in a loss of information or reputation.

*   **Penetration Testing (Pen Testing):** Simulating a cyberattack against your computer system to check for exploitable vulnerabilities.
*   **SAST (Static Application Security Testing):** Analyzing source code to find security vulnerabilities (White Box).
*   **DAST (Dynamic Application Security Testing):** Analyzing a running application to find vulnerabilities (Black Box).
*   **Tools:** OWASP ZAP, Burp Suite, SonarQube.

## 11. Fuzz Testing

Fuzz testing (or Fuzzing) is an automated software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program.

*   **Goal:** To crash the program or trigger unexpected behavior (memory leaks, assertion failures) to find bugs and security vulnerabilities.
*   **How it works:** A "fuzzer" generates massive amounts of random data and feeds it to the target application.
*   **Use Case:** Critical for security-sensitive applications (parsers, protocols, kernels) where handling malformed input correctly is vital.
*   **Tools:** AFL (American Fuzzy Lop), libFuzzer.

## 12. Mutation Testing

Mutation testing is a fault-based testing technique used to evaluate the quality of a test suite. It involves modifying a program in small ways (creating "mutants") and running the test suite against the mutated code.

*   **Goal:** To ensure your tests are actually testing the code. If a test suite passes even when the code is broken (mutated), the tests are weak.
*   **Killed vs. Survived:**
    *   **Killed:** The test suite fails when running against the mutant. (Good).
    *   **Survived:** The test suite still passes despite the code change. (Bad).
*   **Example:** Changing an operator `a + b` to `a - b`. If no test fails, you have a gap in coverage.
*   **Tools:** Stryker (JavaScript/TypeScript/C#), PIT (Java).

## 13. Property Based Testing

Property based testing is a testing technique where, instead of writing specific test cases (e.g., `add(1, 2) == 3`), you define general properties that your code should satisfy for a wide range of inputs.

*   **Concept:** The test framework generates hundreds of random inputs to verify the property holds true.
*   **Example:** For a sorting function, a property might be: "For any list of integers, the output list should have the same length as the input list, and every element should be less than or equal to the next element."
*   **Shrinking:** If a failure is found (e.g., with a list of 100 items), the framework tries to find the smallest possible input that still causes the failure (e.g., a list of 2 items) to make debugging easier.
*   **Tools:** FastCheck (JavaScript), Hypothesis (Python), QuickCheck (Haskell/Rust).

## 14. Shift Left Testing

Shift Left Testing is an approach to software testing and system testing in which testing is performed earlier in the lifecycle (i.e., moved to the "left" on the project timeline).

*   **Goal:** To find and prevent defects early in the software delivery process.
*   **Concept:** Instead of waiting for the "Testing Phase" at the end of the waterfall, testing happens continuously during requirements, design, and coding.
*   **Benefits:**
    *   **Lower Cost:** Fixing a bug during design costs significantly less than fixing it in production.
    *   **Higher Quality:** Continuous feedback leads to better code.
    *   **Faster Delivery:** Reduces the bottleneck of a massive testing phase at the end.

## 15. A/B Testing

A/B testing (also known as split testing) is a user experience research methodology. It consists of a randomized experiment with two variants, A and B.

*   **Goal:** To determine which of the two variants is more effective (e.g., higher conversion rate, better engagement).
*   **Process:**
    1.  **Variant A (Control):** The current version of the feature.
    2.  **Variant B (Treatment):** The modified version.
    3.  **Traffic Split:** Users are randomly assigned to A or B.
    4.  **Analysis:** Statistical analysis determines if the difference in metrics is significant.
*   **Not strictly "Bug" testing:** Unlike other testing types here, A/B testing is about *product* validation, not *code* correctness. However, it requires robust infrastructure to implement safely.

## 16. Snapshot Testing

Snapshot testing is a technique used to make sure your UI does not change unexpectedly. It captures a code component, renders it, and stores a reference image or text file (the snapshot) alongside the test.

*   **How it works:**
    1.  **First Run:** The test runner renders the component and saves the output to a file (e.g., `__snapshots__/Header.test.js.snap`).
    2.  **Subsequent Runs:** The runner renders the component again and compares it to the stored snapshot.
    3.  **Failure:** If they differ, the test fails. This means either the code is broken, or the UI change was intentional and the snapshot needs to be updated.
*   **Use Case:** Very popular in React development (using Jest) to catch accidental CSS or structure changes.
*   **Tools:** Jest.

[[programming/debugging-techniques]]
[[programming/ci-cd-pipelines]]
[[programming/cybersecurity-basics]]