notes

Log | Files | Refs | README

unit_testing.md (3503B)


      1 # Unit Testing
      2 
      3 ## Writing Isolated and Focused Unit Tests
      4 
      5 Unit tests should be **isolated and focused**, testing one small, well-defined
      6 unit of functionality at a time. Each test should verify a single behavior
      7 without relying on other parts of the system. When tests are too broad or
      8 tightly coupled across components, they become brittle — a minor change in one
      9 area can cause unrelated tests to fail.
     10 
     11 To ensure reliability and maintainability, unit tests should:
     12 
     13 - Run independently of external systems or global state.
     14 - Use **mocks or stubs** to replace dependencies.
     15 - Follow the
     16   [**Arrange–Act–Assert**](/test_driven_development/arrange_act_assert.md)
     17   pattern for clarity.
     18 - Be **deterministic** and **fast** so they can run often during development.
     19 
     20 Writing tests this way builds confidence in each unit, simplifies debugging, and
     21 supports modular, testable code design.
     22 
     23 ---
     24 
     25 ## Enabling CI/CD Test Environments with Isolated Resources
     26 
     27 For continuous integration workflows such as **GitHub Actions** or **Forgejo**,
     28 unit tests should run within a fully isolated and reproducible environment. This
     29 means the application should be capable of **spinning up a dedicated test
     30 PostgreSQL instance** and a **test server API** that operates on a separate port
     31 with its own **test-specific environment variables**.
     32 
     33 These configurations ensure that test runs do not interfere with production or
     34 staging databases. The test infrastructure should start up quickly and shut down
     35 cleanly as part of the CI/CD pipeline, allowing automated workflows to execute
     36 the full test suite independently for every build or pull request. This approach
     37 guarantees repeatable, safe testing while maintaining complete separation
     38 between test and production systems.
     39 
     40 ---
     41 
     42 ### Question: What's the difference between intergation tests and unit tests?
     43 
     44 Unit tests focus on small, isolated pieces of code and run very fast, so they
     45 give precise, quick feedback and make it easy to iterate or refactor without
     46 breaking unrelated behavior. In contrast, integration tests exercise multiple
     47 components together (like API, database, and services) to verify real workflows,
     48 which provides higher confidence that the system behaves correctly as a whole.
     49 
     50 The tradeoff is that integration tests are slower, more complex to set up, and
     51 failures can be harder to diagnose, since a small change in one part of the
     52 system can break a test somewhere else. Because of this, you typically rely on
     53 unit tests for rapid development of specific functionality, and use integration
     54 tests more sparingly to ensure that the integrated application still works end
     55 to end.
     56 
     57 ---
     58 
     59 ## Abstractions and Patterns to Keep Tests Stable
     60 
     61 This is where **interfaces** (traits), the facade and adapter patterns,
     62 dependency injection, and internal mutability come in. When a piece of code is
     63 wrapped behind a stable trait or adapter interface, the call sites that depend
     64 on it can remain unchanged, while other developers are free to change the
     65 internals of the wrapped functions without breaking those callers. This gives
     66 more confidence in integration testing when integrating with different external
     67 systems, call sites, or frontends, because the surface contracts stay stable
     68 even as implementations evolve. These abstraction layers reinforce separation of
     69 concerns between core functions, business logic, infrastructure, and UI/API
     70 layers, so each layer can be unit-tested in isolation and then composed
     71 predictably in integration tests.