【问题标题】:Are mocks and stubs implementation details?Are mocks and stubs implementation details?
【发布时间】:2022-12-01 21:11:15
【问题描述】:

I have read that with TDD we should approach the entity (function, class etc) under test from the perspective of the user/caller of the component. The gist being to focus on public "interface". This in turn would drive the design and help reason about the design earlier.
But when we need to introduce mocks and stubs into our tests, isn't that an implementation detail?
Why would the "user" care about other entities are supposed to be there?
E.g.
how to start writing a test for the PlaceOrder service which should check with the credit card service if the user has enough money? Putting a mock from the credit card service whilst writing a test from the perspective of the PlaceOrder client looks out of place now - because it is an implementation detail; our PlaceOrder may call the credit card for each user or it can simply have a cash with scores provided at the creation time.

【问题讨论】:

  • "how to start writing a test for the PlaceOrder service which should check with the credit card service if the user has enough money?" Remember that you can have different payment processors. PayPal, AmazonPay, other ones. They should expose the same interface, though. So youdon't carewhat the user is paying with - if they don't have enough money through PayPal, you'd expect the same behaviour if they didn't have enough money through AmazonPay. At the end of the day, the PlaceOrder should just respond to "not enough money". Shouldn't matter if the message comes from a mock or not.

标签: jestjs tdd bdd


【解决方案1】:

It's not clear-cut. As a catch-phrase says:Tests are specifications.

When you use Test Doubles you are, indeed, specifying how your System Under Test (SUT) ought to interact with its dependencies.

I agree that this isusuallyan implementation detail, but there will typically be a few dependencies of a more architectural character.

A common example is an email gateway. If your SUT should send email, that's an observable side effect that the user (or some other stakeholder) cares about.

While you can (and perhaps should?) also run full systems tests that verify that certain conditions produce real emails that land in certain real mailboxes, such test cases are difficult to automate.

Inserting a Test Double that can take the place of an email gateway and verify that the correct message was delivered to the gateway is not only an implementation detail, but an important part of the overall system. In such cases, using a Test Double makes sense.

Yes, Test Doubles specify behaviour, but sometimes, that's exactly what you want.

How much you should rely on this kind of design is an architectural choice. In addition to sending emails, you might choose to explicitly specify that a certain SUT ought to place a message on a durable queue.

You can create entire systems based on asynchronous messaging, which could imply that it'd be architecturally sound to let tests rely on Test Doubles.

In short, I find it a useful heuristic to use Test Doubles for architectural components, and rely mostly on testing pure functions for everything else.

For something like an order service, I wouldn't let the order service contact the payment gateway. Rather, I'd implement the order service operations as pure functions, and either pass in a payment token as function arguments, or let the output of functions trigger a payment action.

The book Domain Modeling Made Functional contains lots of good information about this kind of architecture.

On the other hand, the book Growing Object-Oriented Software, Guided by Tests contains many good examples of how to use Test Doubles to specify desired behaviour.

Perhaps you'll also find my article From interaction-based to state-based testing useful.

In summary:Tests are specifications.Test Doubles are specifications. Use them to specify the observable behaviour of the system. Try to avoid using them to specify implementation details.

【讨论】:

    猜你喜欢
    • 2011-11-11
    • 1970-01-01
    • 2022-12-28
    • 2021-01-15
    • 2022-12-26
    • 1970-01-01
    • 2019-06-04
    • 2022-12-02
    • 2022-12-01
    相关资源
    最近更新 更多