Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling unmocked method seems to causes mocking attempt on return value #3876

Closed
StuTheBearded opened this issue Sep 27, 2019 · 1 comment
Closed
Labels
type/bug Something is broken

Comments

@StuTheBearded
Copy link

Q A
PHPUnit version 8.3.4
PHP version 7.3.9
Installation Method Composer

Summary

Class "myEntity" is declared "final" and cannot be mocked. Essentially if one makes a call to a method on which isn't mocked it appears as though phpunit will try to mock that method onto a parameter passed to the mock object with the previously mentioned none existant method

Current behavior

Create a mock object and mock a single method which takes a parameter which returns that same parameter with the use of willReturn() and sets an expectation to call it at least once. If you call the a method which you didn't mock and pass the parameter to it phpunit does something very odd it looks like its trying to modify the parameter I passed. The test however still passes and it should certainly fail. This behavior seems similar to bug #3607

How to reproduce

    class OddMockingtest extends \PHPUnit\Framework\TestCase {

        public function testCallingUnmockedMethodCausesMockAttemptOnParameter() {
            $entity = new myEntity();

            $mock = $this->getMockBuilder(mytestInterface::class)
                          ->setMethods(['update'])
                          ->getMockForAbstractClass();

            $mock->expects($this->atLeastOnce())
                 ->method('update')
                 ->willReturn($entity);

            $returned = $mock->insert($entity);
            $this->assertEquals($entity, $returned);
        }
    }

    interface mytestInterface {
        public function insert(myEntity $entity): myEntity;
        public function update(myEntity $entity): myEntity;
    }

    final class myEntity {

        private $data = '';

        public function set(string $data) {
            $this->data = $data;
        }

        public function get() {
            return $this->data;
        }
    }

Expected behavior

Currently this test passed and it should certainly fail. I think it should also throw an error due to an unmet expectation and not to try to alter any passed parameters

@StuTheBearded StuTheBearded added the type/bug Something is broken label Sep 27, 2019
@MichelHartmann
Copy link
Contributor

I just investigated your issue and came to the conclusion, that this is more a misunderstanding than a bug.

When you take a look at the documentation on https://phpunit.readthedocs.io/en/8.4/test-doubles.html#mocking-traits-and-abstract-classes:

The getMockForAbstractClass() method returns a mock object for an abstract class. All abstract methods of the given abstract class are mocked. This allows for testing the concrete methods of an abstract class.

Even if you use setMethod() to specify a subset of methods to mock, getMockForAbstractClass() will mock all abstract methods. (Otherwise the class could not be instantiated.)
So your expectation, that the method insert() is not mocked is not valid. And once PHPUnit tries to fulfill it's duty to dynamically create a return object for that method it will fail with the expected message Class "myEntity" is declared "final" and cannot be mocked..
Please remember: PHPUnit has no means to know that you expect mytestInterface::update() to use the parameter as the return value unless you configure the mock accordingly. That's why it tires to "magically" create a new object of type myEntity.

I hope this explanation can remove your concerns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/bug Something is broken
Projects
None yet
Development

No branches or pull requests

3 participants