> The domain that exports the API should also provide a high fidelity test double that is a fake/in memory implementation (not a mock!)
Not a mock? But that's exactly what a mock is: An implementation that isn't authentic, but that doesn't try to deceive. In other words, something that behaves just like the "real thing" (to the extent that matters), but is not authentically the "real thing". Hence the name.
There are different definitions of the term "mock". You described the generic usage where "mock" is a catch-all for "not the real thing", but there are several terms in this space to refer to more precise concepts.
What I've seen:
* "test double" - a catch-all term for "not the real thing". What you called a "mock". But this phrasing is more general so the term "mock" can be used elsewhere.
* "fake" - a simplified implementation, complex enough to mimic real behavior. It probably uses a lot of the real thing under the hood, but with unnecessary testing-related features removed. ie: a real database that only runs in memory.
* "stub" - a very thin shim that only provides look-up style responses. Basically a map of which inputs produce which outputs.
* "mock" - an object that has expectations about how it is to be used. It encodes some test logic itself.
The Go ecosystem seems to prefer avoiding test objects that encode expectations about how they are used and the community uses the term "mock" specifically to refer to that. This is why you hear "don't use mocks in Go". It refers to a specific type of test double.
By these definitions, OP was referring to a "fake". And I agree with OP that there is much benefit to providing canonical test fakes, so long as you don't lock users into only using your test fake because it will fall short of someone's needs at some point.
Unfortunately there's no authoritative source for these terms (that I'm aware of), so there's always arguing about what exactly words mean.
My best guess is that software development co-opted the term "mock" from the vocabulary of other fields, and the folks who were into formalities used the term for a more specific definition, but the software dev discipline doesn't follow much formal vocabulary and a healthy portion of devs intuitively use the term "mock" generically. (I myself was in the field for years before I encountered any formal vocabulary on the topic.)
> "mock" - an object that has expectations about how it is to be used. It encodes some test logic itself.*
Something doesn't add up. Your link claims that mock originated from XP/TDD, but mock as you describe here violates the core principles of TDD. It also doesn't fit the general definition of mock, whereas what you described originally does.
Beck seemed to describe a mock as something that:
1. Imitates the real object.
2. Records how it is used.
3. Allows you to assert expectations on it.
#2 and #3 sound much like what is sometimes referred to as a "spy". This does not speak to the test logic being in the object itself. But spies do not satisfy #1. So it is seems clear that what Beck was thinking of is more like, say, an in-memory database implementation where it:
1. Behaves like a storage-backed database.
2. Records changes in state. (e.g. update record)
3. Allows you to make assertions on that change in state. (e.g. fetch record and assert it has changed)
I'm quite sure Fowler's got it wrong here. He admits to being wrong about it before, so the odds are that he still is. The compounding evidence is not in his favour.
Certainly if anyone used what you call a mock in their code you'd mock (as in make fun of) them for doing so. It is not a good idea. But I'm not sure that equates to the pattern itself also being called a mock.
I think this is the crux that separates Fowler's mock, spy, and stub: Who places what expectations.
Fowler's mock is about testing behavioral interaction with the test double. In Fowler's example, the mock is given the expectations about what APIs will be used (warehouseMock.expects()) then those expectations are later asserted (warehouseMock.Verify()).
Behavioral interaction encodes some of the implementation detail. It asserts that certain calls must be made, possibly with certain parameters, and possibly in a certain order. The danger is that it is somewhat implementation specific. A refactoring that keeps the input/output stable but achieves the goal through different means must still update the tests, which is generally a red flag.
This is what my original statement referred to, the interaction verification. Generally the expectations are encoded in the mock itself for ergonomics sake, but it's technically possible to do the interaction testing without putting it in the mock. Regardless of exactly where the assertion logic goes, if the test double is testing its interactions then it is a Fowler mock.
(As an example: An anti-pattern I've seen in Python mocks is asserting that every mocked object function call happens. The tests end up being basically a simplified version of the original code and logic flaws in the code can be copied over to the tests because they're basically written as a pseudo stack trace of the test case.)
In contrast, a stub is not asserting any interaction behavior. In fact it asserts nothing and lets the test logic itself assert expectations by calling the API. ie:
> 3. Allows you to make assertions on that change in state. (e.g. fetch record and assert it has changed)
These concepts seem distinct enough to make mock a simple.
Fowler's spy seems to sit half-way between mock and stub: It doesn't assert detailed interaction expectations, but it does check some of the internals. A spy is open-ended, you can write any sort of validation logic, whereas a mock is specifically about how it is used.
I have used spys in Go basically whenever I need to verify side effect behavior that is not attainable via the main API.
By Fowler's definition, nocks are a niche test double and I suspect that what many folks would call a mock are not technically a mock.
Not a mock? But that's exactly what a mock is: An implementation that isn't authentic, but that doesn't try to deceive. In other words, something that behaves just like the "real thing" (to the extent that matters), but is not authentically the "real thing". Hence the name.