social-unit-tests

Social Tests

What

  • Also known as

    • documentation tests

    • acceptance unit tests

    • module tests

  • test that verifies the behavior of a unit (e.g., a class or a function) in collaboration with its real dependencies rather than mocking them

    • Any infrastructure calls (ie http, message bus, files etc) are stubbed/faked out

  • Writing tests without using mocks/spies, but using stubbed out (test implementation of dependencies with possible extra logic or static factories in prod code used only for test), to test several classes at a time.

  • Used in testing the core of hexagonal architecture

    • With business rules being tested

    • with the ports implementing fakes

    • Adapters not tested

  • Avoiding use of mocks to verify

    • as these are dealt with ....????

  • Care about the output

    • ie the reponse

  • This is a testing pattern, and not a hammer

    • should be have reasons to use, and use other tests type

Characteristics

  • Real Collaborators: The unit interacts with its actual dependencies.

  • Integration-Like: Tests a small part of the system, often a subset of the domain or application logic, an aggregate

  • Behavior-Oriented: Focuses on the outcome of the collaboration rather than internal implementation details.

  • Limited Scope: While broader than solitary tests, they do not span the entire application stack (e.g., they exclude the database or external systems).

Advantages

  • Thus allowing ease of refactoring, as code is not tied test implementation.

  • validate the actual collaboration between multiple units, rather than have fine grained tests

  • When testing the business logic in a domain-driven design or hexagonal architecture, sociable tests ensure domain entities and value objects work together correctly.

    • Example: An aggregate root interacting with child entities to enforce invariants.

  • Good for handling complex interactions.

    • When interactions between multiple components are too complex to mock effectively without missing edge cases or creating brittle tests.

    • Example: Validating the flow of a process where components communicate heavily (e.g., an order placement pipeline).

  • Avoiding Mocking Overhead:

    • Sociable tests are beneficial when mocking dependencies would introduce unnecessary complexity or make the test fragile.

    • Example: A calculator service that depends on multiple reusable utility classes.

  • Avoid testing implementation, and focus on testing behaviour

  • Focused on business rules:

    • avoid testing situations that will never happen in a business flow,

    • example: as the calling object calling it might never have the state that could be used in the test.

    • Can be used with BDD language and be read by business

  • Avoid mocking/stubbing business rules that might return the wrong thing

  • Less end to end acceptance tests

Disadvantages/When not to use

  • Testing Behavior with External Systems or boundaries:

    • Avoid sociable tests for interactions with databases, file systems, network APIs, or other external systems.

    • This is integration tests

  • Highly Independent Units:

    • If a unit has minimal or no interaction with other components, a solitary unit test is sufficient.

      • Example: Testing a string utility function or a pure algorithm.

  • Complex algorithms/rules:

    • Can start off with a social test, but might be easier to unit test the rules/algorithm separately and easier (ie remove dependency graph)

  • Non-Deterministic Behavior:

    • unsuitable for systems with non-deterministic behavior (e.g., random number generation, time-based behavior) unless the randomness can be controlled.

    • Example: A test that depends on real-world timing or randomness.

  • Complex Dependency Graphs

    • hen the dependency graph is too complex, as this can lead to brittle and slow tests.

    • Example: A service relying on many indirect dependencies through deeply nested calls.

    • This can be handled via a factory

  • Performance Concerns:

    • Can be slow due to complex setups or large numbers of interacting components, they may not be ideal for fast feedback loops.

  • Can be harder to isolate failures

    • When multiple units are involved, it can be difficult to pinpoint which unit is causing a test failure.

    • Will need to use a debugger more

  • Limited Fault Injection

    • It’s harder to simulate specific edge cases or failure scenarios compared to solitary tests with mocks or stubs.

    • Example: Testing how a service handles an exception from a dependency may require significant effort to trigger that failure

  • Overlap with Integration Tests

    • may duplicate the purpose of integration tests, leading to redundancy and wasted effort.

  • Reduced Control Over Dependencies

    • use real dependencies, which means they lack the fine-grained control provided by mocks or fakes.

    • A dependency that randomly changes its state (e.g., based on system time) might cause sporadic test failures.

    • Real dependencies may introduce non-determinism (e.g., caching, threading, random inputs), making tests unreliable.

  • Potential Over-Reliance

    • Overusing sociable tests can lead to insufficient solitary unit tests, which are crucial for pinpointing issues within individual units.

Examples

  • https://www.jamesshore.com/v2/projects/testing-without-mocks/testing-without-mocks

Last updated