How to mock a constructor with Mockery

VaclavSir picture VaclavSir · Jun 1, 2015 · Viewed 8.5k times · Source

I need to test, that the code creates a new instance of a class with certain parameters:

$bar = new ProgressBar($output, $size);

I tried to create an alias mock and set an expectation for the __construct method, but it didn't work:

$progressBar = \Mockery::mock('alias:' . ProgressBar::class);
$progressBar->shouldReceive('__construct')
    ->with(\Mockery::type(OutputInterface::class), 3)
    ->once();

This expectation is never met:

Mockery\Exception\InvalidCountException: Method __construct(object(Mockery\Matcher\Type), 3) from Symfony\Component\Console\Helper\ProgressBar should be called exactly 1 times but called 0 times.

Do you know any other way how to test this with Mockery?

Answer

Alexander Matrosov picture Alexander Matrosov · Dec 29, 2017

Well you cannot mock constructor. Instead you need slightly modify your production code. As I can guess from decription you have something like this:

class Foo {
    public function bar(){
        $bar = new ProgressBar($output, $size);
    }
}

class ProgressBar{
    public function __construct($output, $size){
        $this->output = $output;
        $this->size = $size;
    }
}

Which is not best code in the world, because we have coupled dependance. (Which is totally ok if ProgressBar is Value Object for example).

First of all you should to test ProgressBar separately from Foo. Because then you test Foo you do not need care how ProgressBar works. You know it works, you have tests for this.

But if you still want to test it instantiation (for any reason) here is two ways. For both ways you need extract new ProggresBar

class Foo {
    public function bar(){
        $bar = $this->getBar($output, $size);
    }

    public function getBar($output, $size){
        return new ProgressBar($output, $size)
    }
}

Way 1:

class FooTest{
    public function test(){
        $foo = new Foo();
        $this->assertInstanceOf(ProgressBar::class, $foo->getBar(\Mockery::type(OutputInterface::class), 3));
    }
}

Way 2:

class FooTest{
    public function test(){
        $mock = \Mockery::mock(Foo::class)->makePartial();
        $mock->shouldReceive('getBar')
            ->with(\Mockery::type(OutputInterface::class), 3)
            ->once();
    }
}

Happy testing!