diff --git a/library/Mockery/Expectation.php b/library/Mockery/Expectation.php index a365de469..e73a0885c 100644 --- a/library/Mockery/Expectation.php +++ b/library/Mockery/Expectation.php @@ -19,6 +19,7 @@ class Expectation implements ExpectationInterface { + public const ERROR_ZERO_INVOCATION = 'shouldNotReceive(), never(), times(0) chaining additional invocation count methods has been deprecated and will throw an exception in a future version of Mockery'; /** * Mock object to which this expectation belongs * @@ -40,6 +41,13 @@ class Expectation implements ExpectationInterface */ protected $_because = null; + /** + * Expected count of calls to this expectation + * + * @var int + */ + protected $_expectedCount = -1; + /** * Arguments expected by this expectation * @@ -719,6 +727,18 @@ public function times($limit = null) if (!is_int($limit)) { throw new \InvalidArgumentException('The passed Times limit should be an integer value'); } + + if ($this->_expectedCount === 0) { + @trigger_error(self::ERROR_ZERO_INVOCATION, E_USER_DEPRECATED); + // throw new \InvalidArgumentException(self::ERROR_ZERO_INVOCATION); + } + + if ($limit === 0) { + $this->_countValidators = []; + } + + $this->_expectedCount = $limit; + $this->_countValidators[$this->_countValidatorClass] = new $this->_countValidatorClass($this, $limit); if ('Mockery\CountValidator\Exact' !== $this->_countValidatorClass) { diff --git a/tests/Unit/Regression/Issue1328Test.php b/tests/Unit/Regression/Issue1328Test.php new file mode 100644 index 000000000..ef5467060 --- /dev/null +++ b/tests/Unit/Regression/Issue1328Test.php @@ -0,0 +1,70 @@ +expectException(InvalidCountException::class); + + $mock = Mockery::mock(DateTime::class); + + $mock->shouldNotReceive("format"); + + $mock->format("Y"); + + Mockery::close(); + } + + public function testThrowsInvalidArgumentExceptionWhenInvocationCountChanges(): void + { + set_error_handler( + static function (int $errorCode, string $errorMessage): void { + restore_error_handler(); + throw new InvalidArgumentException($errorMessage, $errorCode); + }, + E_ALL + ); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage(Expectation::ERROR_ZERO_INVOCATION); + + $mock = Mockery::mock(DateTime::class); + + $mock->shouldNotReceive("format")->once(); + + $mock->format("Y"); + + Mockery::close(); + } + + public function testThrowsInvalidArgumentExceptionForChainingAdditionalInvocationCountMethod(): void + { + set_error_handler( + static function (int $errorCode, string $errorMessage): void { + restore_error_handler(); + throw new InvalidArgumentException($errorMessage, $errorCode); + }, + E_ALL + ); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage(Expectation::ERROR_ZERO_INVOCATION); + + $mock = Mockery::mock(DateTime::class); + + $mock->shouldNotReceive("format")->times(0); + + $mock->format("Y"); + + Mockery::close(); + } +}