Skip to content

willReturnMap() fails with nullable parameters when their default is null and no argument is passed for them #6174

@frozenbrain

Description

@frozenbrain
Q A
PHPUnit version 12.1.0
PHP version 8.4.3
Installation Method Composer

Summary

The handling of default values in willReturnMap doesn't seem to take into account that parameters and/or default values may be null.

Current behavior

  • Omitting a default null parameter in willReturnMap does not work and causes the mapping to never match (see methodNullDefault tests below)
  • Omitting a default non-null parameter does work, however doing so breaks the handling of other null parameters, even without default values (see methodStringDefault tests below)

Therefore the only way to get the tests to pass is to provide the full set of parameters.

How to reproduce

<?php

namespace App;

interface SomeInterface
{
    public function methodNullDefault(?string $param, ?string $nullDefault = null): string;

    public function methodStringDefault(?string $param, ?string $stringDefault = 'something'): string;
}
<?php

use App\SomeInterface;
use PHPUnit\Framework\TestCase;

class SomeInterfaceTest extends TestCase
{
    public function testMethodNullDefaultPasses(): void
    {
        $mock = $this->createMock(SomeInterface::class);
        $mock->method('methodNullDefault')->willReturnMap([
            ['A', null, 'ret'],
        ]);

        self::assertSame('ret', $mock->methodNullDefault('A'));
    }

    public function testMethodNullDefaultFails(): void
    {
        $mock = $this->createMock(SomeInterface::class);
        $mock->method('methodNullDefault')->willReturnMap([
            ['A', 'ret'],
        ]);

        self::assertSame('ret', $mock->methodNullDefault('A'));
    }

    public function testMethodStringDefaultPasses(): void
    {
        $mock = $this->createMock(SomeInterface::class);
        $mock->method('methodStringDefault')->willReturnMap([
            ['A', 'ret'],
        ]);

        self::assertSame('ret', $mock->methodStringDefault('A'));
    }

    public function testMethodStringDefaultFails(): void
    {
        $mock = $this->createMock(SomeInterface::class);
        $mock->method('methodStringDefault')->willReturnMap([
            [null, 'ret'],
        ]);

        self::assertSame('ret', $mock->methodStringDefault(null));
    }
}

Expected behavior

All of the above tests pass.

I believe this is caused by the two isset calls in

which I would guess should be array_key_exists instead.

Output of composer info | sort

myclabs/deep-copy            1.13.0 Create deep copies (clones) of your objects
nikic/php-parser             5.4.0  A PHP parser written in PHP
phar-io/manifest             2.0.4  Component for reading phar.io manifest information from a PHP Archive (PHAR)
phar-io/version              3.2.1  Library for handling version information and constraints
phpunit/php-code-coverage    12.1.2 Library that provides collection, processing, and rendering functionality for PHP code coverage information.
phpunit/php-file-iterator    6.0.0  FilterIterator implementation that filters files based on a list of suffixes.
phpunit/php-invoker          6.0.0  Invoke callables with a timeout
phpunit/php-text-template    5.0.0  Simple template engine.
phpunit/php-timer            8.0.0  Utility class for timing
phpunit/phpunit              12.1.0 The PHP Unit Testing framework.
sebastian/cli-parser         4.0.0  Library for parsing CLI options
sebastian/comparator         7.0.1  Provides the functionality to compare PHP values for equality
sebastian/complexity         5.0.0  Library for calculating the complexity of PHP code units
sebastian/diff               7.0.0  Diff implementation
sebastian/environment        8.0.0  Provides functionality to handle HHVM/PHP environments
sebastian/exporter           7.0.0  Provides the functionality to export PHP variables for visualization
sebastian/global-state       8.0.0  Snapshotting of global state
sebastian/lines-of-code      4.0.0  Library for counting the lines of code in PHP source code
sebastian/object-enumerator  7.0.0  Traverses array structures and object graphs to enumerate all referenced objects
sebastian/object-reflector   5.0.0  Allows reflection of object attributes, including inherited and non-public ones
sebastian/recursion-context  7.0.0  Provides functionality to recursively process PHP variables
sebastian/type               6.0.2  Collection of value objects that represent the types of the PHP type system
sebastian/version            6.0.0  Library that helps with managing the version number of Git-hosted PHP projects
staabm/side-effects-detector 1.0.5  A static analysis tool to detect side effects in PHP code
theseer/tokenizer            1.2.3  A small library for converting tokenized PHP source code into XML and potentially other formats

Metadata

Metadata

Labels

feature/test-doublesTest Stubs and Mock Objectstype/bugSomething is brokenversion/11Something affects PHPUnit 11version/12Something affects PHPUnit 12

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions