Page cover image

Unit Tests

Unit testing is a technique for testing the smallest units of an application to ensure they work as intended. The smallest unit of an application depends on the language: in functional languages, it's a function, while in object-oriented paradigms, it's a method or a class. The requirements for unit tests are that they should be isolated, fast, and consistent, meaning they should give the same result every time they are executed.

When writing unit tests, especially for components that interact with external systems or complex dependencies, isolating the unit under test is important. This is where fakes, mocks, and stubs come into play. The more complex a project is the greater the need for using fakes, mocks, and stubs for testing. Testing applications with complex dependencies can be tricky. Fakes, mocks, and stubs make this process easier and improve the quality, reliability, and simplicity of tests.

Fakes

Fakes are objects that have working implementations but are simpler compared to the production versions. These simplified versions are often used for integration testing to avoid the need for complex setups or external dependencies. For instance, an in-memory database (e.g. H2) can be used to store data rather than an actual production database implementation, providing quicker, simpler, and less resource-intensive tests.

Fakes are also a tool for prototyping and experimentation, allowing developers to defer decisions about final deployments, such as database design. Fakes help simulate real-world complex scenarios effectively and efficiently.

Mocks

Mocks are specialized objects that record the calls they receive, enabling the verification of interactions during tests. They are used when you need to confirm that certain methods were called, without invoking actual production code.

For example, a mock mail service such as shown below can be used to ensure that the sending method was called and the necessary functionality was tested, without actually sending emails during the test execution.

Mocks are prepared with expectations to ensure consistent and deterministic behavior during tests. They do not execute real logic but instead focus on verifying that a specific interaction occurred. In unit tests, mocks can replace dependencies, allowing the testing of business logic in isolation. Mocking frameworks and libraries often manage the lifecycle of mock objects, making it easier for developers to integrate them into their test suites and ensure thorough test coverage.

Stubs

Stubs are objects that return predefined data in response to calls made during tests. They are particularly useful when you cannot or do not want to use real objects that might have undesirable side effects or are not available.

For example, instead of querying a database, a stub can return fixed data, ensuring that tests are consistent and predictable. Stubs provide a controlled environment for testing by delivering specific responses without performing any real logic. Unlike mocks, stubs do not record interactions and cannot return different values based on varying inputs. They are simple and effective for ensuring that certain conditions are met during tests without the overhead of managing real dependencies.

Comparison

FakesMocksStubs

Implementation

Simplified implementation of certain functionality.

Pre-programmed with expectations providing deterministic behavior.

Provide canned responses or predefined behavior without real implementation.

Verify interactions

Do not verify interactions.

Configured to verify the occurrence of expected interaction.

Do not verify interactions.

Flexibility

No flexibility during tests.

Very flexible as multiple expectations and responses can be configured.

Less flexible in terms of behavior since they provide predefined responses.

Last updated