A mock asserts an interaction; a stub only answers

PHPUnit builds both with the same method, which is why the distinction gets lost. A stub exists to let the test proceed — it returns a value. A mock exists to assert that a call happened, with particular arguments, a particular number of times.

// stub: the gateway just needs to answer
$gateway = $this->createMock(Gateway::class);
$gateway->method('charge')->willReturn(new Receipt('ok'));

// mock: the assertion IS that charge was called once, with this amount
$gateway = $this->createMock(Gateway::class);
$gateway->expects($this->once())
        ->method('charge')
        ->with($this->equalTo(4900));

expects() is the line that turns one into the other. Over-using mocks produces tests that assert how the code is written rather than what it does, so they fail on every refactor while catching nothing. Mock at the edges — the payment gateway, the mailer — and stub everything in between.