diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml
index 9f63d36fb39..8093b9697ae 100644
--- a/.psalm/baseline.xml
+++ b/.psalm/baseline.xml
@@ -223,11 +223,15 @@
$this->expectedException
-
+
0
$header
+ $originalClassName
+
+ $originalClassName
+
null
$beStrictAboutChangesToGlobalState
diff --git a/src/Framework/MockObject/MockBuilder.php b/src/Framework/MockObject/MockBuilder.php
index ff09628e5ff..e17114b572b 100644
--- a/src/Framework/MockObject/MockBuilder.php
+++ b/src/Framework/MockObject/MockBuilder.php
@@ -182,6 +182,8 @@ public function setMethods(array $methods = null): self
{
$this->methods = $methods;
+ $this->alreadyUsedMockMethodConfiguration = true;
+
return $this;
}
diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php
index 7c554249f82..ccdcbaedaf2 100644
--- a/src/Framework/TestCase.php
+++ b/src/Framework/TestCase.php
@@ -1388,6 +1388,22 @@ protected function createConfiguredMock($originalClassName, array $configuration
*/
protected function createPartialMock($originalClassName, array $methods): MockObject
{
+ $reflection = new \ReflectionClass($originalClassName);
+
+ $mockedMethodsThatDontExist = \array_filter($methods, function (string $method) use ($reflection) {
+ return !$reflection->hasMethod($method);
+ });
+
+ if ($mockedMethodsThatDontExist) {
+ $this->addWarning(
+ \sprintf(
+ 'createPartialMock called with method(s) %s that do not exist in %s. This will not be allowed in future versions of PHPUnit.',
+ \implode(', ', $mockedMethodsThatDontExist),
+ $originalClassName
+ )
+ );
+ }
+
try {
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
diff --git a/tests/_files/TestWithDifferentStatuses.php b/tests/_files/TestWithDifferentStatuses.php
index e680f6ce2d3..da170332349 100644
--- a/tests/_files/TestWithDifferentStatuses.php
+++ b/tests/_files/TestWithDifferentStatuses.php
@@ -45,4 +45,15 @@ public function testThatAddsAWarning(): void
{
$this->addWarning('Sorry, Dave!');
}
+
+ public function testWithCreatePartialMockWarning(): void
+ {
+ $this->createPartialMock(\Mockable::class, ['mockableMethod', 'fakeMethod1', 'fakeMethod2']);
+ }
+
+ public function testWithCreatePartialMockPassesNoWarning(): void
+ {
+ $mock = $this->createPartialMock(\Mockable::class, ['mockableMethod']);
+ $this->assertNull($mock->mockableMethod());
+ }
}
diff --git a/tests/unit/Framework/MockObject/MockBuilderTest.php b/tests/unit/Framework/MockObject/MockBuilderTest.php
index 7c31c86d49a..158920428c9 100644
--- a/tests/unit/Framework/MockObject/MockBuilderTest.php
+++ b/tests/unit/Framework/MockObject/MockBuilderTest.php
@@ -128,6 +128,46 @@ public function testNotAbleToUseOnlyMethodsAfterAddMethods(): void
->getMock();
}
+ public function testAbleToUseSetMethodsAfterOnlyMethods(): void
+ {
+ $mock = $this->getMockBuilder(Mockable::class)
+ ->onlyMethods(['mockableMethod'])
+ ->setMethods(['mockableMethodWithCrazyName'])
+ ->getMock();
+
+ $this->assertNull($mock->mockableMethodWithCrazyName());
+ }
+
+ public function testAbleToUseSetMethodsAfterAddMethods(): void
+ {
+ $mock = $this->getMockBuilder(Mockable::class)
+ ->addMethods(['notAMethod'])
+ ->setMethods(['mockableMethodWithCrazyName'])
+ ->getMock();
+
+ $this->assertNull($mock->mockableMethodWithCrazyName());
+ }
+
+ public function testNotAbleToUseAddMethodsAfterSetMethods(): void
+ {
+ $this->expectException(RuntimeException::class);
+
+ $this->getMockBuilder(Mockable::class)
+ ->setMethods(['mockableMethod'])
+ ->addMethods(['mockableMethodWithFakeMethod'])
+ ->getMock();
+ }
+
+ public function testNotAbleToUseOnlyMethodsAfterSetMethods(): void
+ {
+ $this->expectException(RuntimeException::class);
+
+ $this->getMockBuilder(Mockable::class)
+ ->setMethods(['mockableMethodWithFakeMethod'])
+ ->onlyMethods(['mockableMethod'])
+ ->getMock();
+ }
+
public function testByDefaultDoesNotPassArgumentsToTheConstructor(): void
{
$mock = $this->getMockBuilder(Mockable::class)->getMock();
diff --git a/tests/unit/Framework/TestCaseTest.php b/tests/unit/Framework/TestCaseTest.php
index 738f60ce082..d410cd8d5ad 100644
--- a/tests/unit/Framework/TestCaseTest.php
+++ b/tests/unit/Framework/TestCaseTest.php
@@ -856,6 +856,26 @@ public function testCreatePartialMockCanMockNoMethods(): void
$this->assertTrue($mock->anotherMockableMethod());
}
+ public function testCreatePartialMockWithFakeMethods(): void
+ {
+ $test = new \TestWithDifferentStatuses('testWithCreatePartialMockWarning');
+
+ $test->run();
+
+ $this->assertSame(BaseTestRunner::STATUS_WARNING, $test->getStatus());
+ $this->assertFalse($test->hasFailed());
+ }
+
+ public function testCreatePartialMockWithRealMethods(): void
+ {
+ $test = new \TestWithDifferentStatuses('testWithCreatePartialMockPassesNoWarning');
+
+ $test->run();
+
+ $this->assertSame(BaseTestRunner::STATUS_PASSED, $test->getStatus());
+ $this->assertFalse($test->hasFailed());
+ }
+
public function testCreateMockSkipsConstructor(): void
{
/** @var \Mockable $mock */