sebastianbergmann/phpunit · error · RuntimeException

At least two interfaces must be specified

Error message

At least two interfaces must be specified

What it means

createMockForIntersectionOfInterfaces()/createStubForIntersectionOfInterfaces() build a test double that satisfies the intersection of several interfaces at once (PHP does not allow runtime intersection types, so PHPUnit generates a named intersection interface). At least two interfaces are required for an intersection to be meaningful; passing fewer causes this RuntimeException.

Source

Thrown at src/Framework/MockObject/Generator/Generator.php:144

        if ($mockObject) {
            assert($object instanceof MockObject);
        } else {
            assert($object instanceof Stub);
        }

        return $object;
    }

    /**
     * @param list<class-string> $interfaces
     *
     * @throws RuntimeException
     * @throws UnknownInterfaceException
     */
    public function testDoubleForInterfaceIntersection(array $interfaces, bool $mockObject, bool $returnValueGeneration = true): MockObject|Stub
    {
        if (count($interfaces) < 2) {
            throw new RuntimeException('At least two interfaces must be specified');
        }

        foreach ($interfaces as $interface) {
            if (!interface_exists($interface)) {
                throw new UnknownInterfaceException($interface);
            }
        }

        sort($interfaces);

        $methods = [];

        foreach ($interfaces as $interface) {
            $methods = array_merge($methods, $this->namesOfMethodsIn($interface));
        }

        if (count(array_unique($methods)) < count($methods)) {
            throw new RuntimeException('Interfaces must not declare the same method');

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Pass at least two existing interfaces: createMockForIntersectionOfInterfaces([A::class, B::class])
  2. If only one interface is needed, use createMock(A::class) instead
  3. When the list is computed, guard it: if (count($interfaces) < 2) { return createMock($interfaces[0]); }

Example fix

// before
$registry = $this->createMockForIntersectionOfInterfaces(
    [CacheInterface::class] // only one
);

// after
$registry = $this->createMockForIntersectionOfInterfaces(
    [CacheInterface::class, LoggerInterface::class]
);
// or, if one suffices:
$registry = $this->createMock(CacheInterface::class);
Defensive patterns

Strategy: validation

Validate before calling

if (count($interfaces) < 2) {
    // Not an intersection: fall back to a plain mock
    $double = $this->createMock($interfaces[0]);
} else {
    $double = $this->createMockForIntersectionOfInterfaces($interfaces);
}

Prevention

When it happens

Trigger: createMockForIntersectionOfInterfaces([SessionHandlerInterface::class]) with a single element, an empty array, or a list where a dynamic/computed list collapsed to one entry (e.g. array_filter removed the second interface, config returned one).

Common situations: Test data built dynamically from configuration or parametrized providers where the second interface is optional and sometimes absent; refactoring from a hand-written combined interface to intersection mocks; copy-paste leftovers where one interface was commented out.

Related errors


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/b0ff732c309a2f55. Report an issue: GitHub.