JB Rainsberger has an article that seems to precisely answer your question : Who Tests The Contract Tests ?
So here's my question : Is it right that when faking a collaborator
which provides indirect inputs to a system under test, one should use
stubs in order to prevent unnoticed interface / contract changes?
Use stubs as opposed to what ?
Mocks ? That doesn't make a big difference, and stubs are more appropriate in that kind of situation anyway, as pointed out by @bryanbcook .
Manual fake classes that don't even implement the same base class as the collaborator ? Sure.
IMO there are 2 kinds of contract changes :
"Hard" changes, i.e. changes in the return type or parameter types of your collaborator's method. These modifications are easy - they can't go unnoticed as your tests won't even compile any more, provided your fake collaborator implements the same interface/base class as the real collaborator.
"Soft" changes, i.e. changes in the conditions under which the collaborator returns a given value or another, changes in the range of values the collaborator can return or accept, in the exceptions that could be thrown, etc. These are more difficult to spot but according to the aforementioned article, they could be avoided by enforcing a strict correspondence between contract tests and collaboration tests.