在Spock框架中何时使用Mock与Stub?兼析Grails文档相关表述
Great questions—let’s break this down clearly, since mixing up mocks and stubs is a common pitfall in testing.
When to Use a Mock in Spock
Mocks are all about verifying interactions between your test subject and its collaborators. You use a mock when you need to prove that the subject is talking to its dependencies the right way—not just that it gets a result, but that it makes the correct calls with the right arguments, at the right time, and the right number of times.
Examples of mock scenarios:
- You’re testing a
UserServiceand want to ensure it callsUserRepository.save(user)exactly once after validating the user. - You need to confirm that a
NotificationSenderis never called if the input data is invalid. - You want to verify that a
PaymentProcessorreceives a charge request with the exact amount and currency specified.
In Spock, you’d use Mock() and add interaction checks in the then: block, like:
def "user service saves valid user once"() { given: def repo = Mock(UserRepository) def service = new UserService(repo: repo) def user = new User(name: "Alice") when: service.createUser(user) then: 1 * repo.save(user) }
When to Use a Stub in Spock
Stubs, on the other hand, are about providing canned responses to let your test subject do its job. You don’t care about how the subject interacts with the stub—you just need the stub to return specific data (or throw exceptions) so you can test how the subject behaves in response to those scenarios.
Examples of stub scenarios:
- You’re testing that a
ProductServicereturns an "out of stock" message when theInventoryClientreports zero quantity. - You want to verify that your
OrderProcessorhandles a timeout error from theShippingAPIby retrying once. - You need to simulate a
UserRepositoryreturning a null user to test how yourAuthServicehandles invalid login attempts.
In Spock, you’d use Stub() and define canned responses in the given: or when: block, like:
def "product service returns out of stock message when inventory is zero"() { given: def inventoryClient = Stub(InventoryClient) inventoryClient.getStock(123) >> 0 def service = new ProductService(inventoryClient: inventoryClient) when: def result = service.checkStock(123) then: result == "Out of stock" }
Breaking Down the Grails Documentation Quote
Let’s unpack that Grails docs statement to make it concrete:
If the test is concerned with proving that the test subject interacts with a collaborator in a particular way, use a mock. If the fact that a collaborator behaves in a certain way exposes a particular behavior in the test subject the outcome of that behavior is what you are testing, use a stub.
- The first part aligns perfectly with mock use cases: your test’s core goal is to confirm interaction details (method calls, arguments, frequency). The test passes only if those interactions happen exactly as expected.
- The second part describes stub use cases: your test focuses on how the subject responds to the collaborator’s behavior. The test’s success depends on the subject’s outcome (like returning the right message or handling an error correctly), not on the specifics of how it called the collaborator.
In short: Mocks verify interactions; stubs provide data to test behavior.
内容的提问来源于stack exchange,提问作者rihabe berrich

