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

Cache for generated Test Double code was not updated for Test Stub and Mock Object distinction #5534

Closed
frankdekker opened this issue Oct 7, 2023 · 3 comments
Assignees
Labels
feature/test-doubles Test Stubs and Mock Objects type/bug Something is broken version/10 Something affects PHPUnit 10

Comments

@frankdekker
Copy link

frankdekker commented Oct 7, 2023

Q A
PHPUnit version 10.4.0
PHP version 8.1
Installation Method Composer

Summary

In PHPUnit 10.4.0 I occassionally get a TestStub class returned instead of a MockObject class as before.

Current behavior

// For any of the return objects another MockObject is created, allowing the following notation to work:
$myClass = $this->createMock(MyClass::class);
$myClass->getTransformer()->getModel()->getData();

How to reproduce

The chaining above fails if in some other test for one of the return objects a TestStub is created. The chained method will then return a TestStub for that class instead of a MockObject, which is not compatible with the method return type and crashes with a type error.

I don't quite understand why, where and how a TestStub is created, but I found the problem where the mix up happens. The problem is because there's a TestStub / MockObject cache, but the generated cache key doesn't differentiate between TestStub and MockObject. See:

Class: PHPUnit\Framework\MockObject\Generator\Generator
Method: generate

Code:

$key = md5(
    $type .
    serialize($methods) .
    serialize($callOriginalClone) .
    serialize($cloneArguments) .
    serialize($callOriginalMethods),
);

My temp fix that seems to work. Add the $mockObject status into the cache key:

$key = md5(
    $type .
    ($mockObject ? 'mock' : 'stub') .
    serialize($methods) .
    serialize($callOriginalClone) .
    serialize($cloneArguments) .
    serialize($callOriginalMethods),
);
@frankdekker frankdekker added type/bug Something is broken version/10 Something affects PHPUnit 10 labels Oct 7, 2023
@sebastianbergmann
Copy link
Owner

Thank you for your report.

Please provide a minimal, self-contained, reproducing test case that shows the problem you are reporting.

Without such a minimal, self-contained, reproducing test case I will not be able to investigate this issue.

@sebastianbergmann sebastianbergmann added status/waiting-for-feedback Waiting for feedback from original reporter feature/test-doubles Test Stubs and Mock Objects labels Oct 7, 2023
@frankdekker
Copy link
Author

Could you give me a hint when a TestStub is created? If I know that, I can create an example reproduction.

@sebastianbergmann sebastianbergmann changed the title A TestStub is returned for a class mock instead of a MockObject. Cache for generated Test Double code was not updated for Test Stub and Mock Object distinction Oct 8, 2023
@sebastianbergmann sebastianbergmann self-assigned this Oct 8, 2023
@sebastianbergmann sebastianbergmann removed the status/waiting-for-feedback Waiting for feedback from original reporter label Oct 8, 2023
@sebastianbergmann
Copy link
Owner

sebastianbergmann commented Oct 8, 2023

createStub($type) is documented to return Stub&$type whereas createMock($type) is documented to return MockObject&$type. Under the hood, however, both createStub($type) and createMock($type) generate and return MockObject&$type for legacy reasons at the moment.

In a future major version of PHPUnit createStub() will be changed to generate and return Stub&$type. This means that expects() will no longer be available on test double objects created using createStub(). For PHPUnit 10.4, the foundation for this change was laid: the test double code generator now uses a flag, $mockObject to control whether a test stub (false) or a mock object (true) should be created. All code paths, except the one used by the return value generator, unconditionally pass true to the test double code generator to maintain backward compatibility.

When a test stub is requested for OriginalType then the name of the generated class is TestStub_OriginalType_random. When a mock object is requested for OriginalType then the name of the generated class is MockObject_OriginalType_random.

However, and like you already noticed, the key for the cache for generated code did not distinguish between test stubs and mock objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature/test-doubles Test Stubs and Mock Objects type/bug Something is broken version/10 Something affects PHPUnit 10
Projects
None yet
Development

No branches or pull requests

2 participants