phpspec / prophecy

Highly opinionated mocking framework for PHP 5.3+
MIT License
8.53k stars 241 forks source link

[Support Request] Mocking methods results in TypeError, what am I doing wrong? #554

Closed jerrac closed 5 months ago

jerrac commented 2 years ago

Apologies if this isn't a good place for support requests. Could you point me to the correct place?

I'm struggling to get Prophecy to work at all. I've read, and skimmed, the readme enough times I'm not registering it anymore, and Google hasn't been any help. So could you point out what I'm missing? I'm really really new to unit testing, so please go easy on me.

First, am I right in believing that Prophecy lets you predict that an object will be created, and lets you mock/stub that object to do what you want?

In my case, I have a class that connects to RabbitMQ. I'd rather not have to connect to RabbitMQ every time I run my tests, so I want to override the parts of the class that do so.

In the method I'm testing, that class is called via new, so I can't just use PHPUnit's stubs/mocks and pass the object into the the method.

Prophecy should let me, essentially, intercept that class and replace it with a mock. Right?

My assumptions checked, here's what I'm trying, and what goes wrong.

Here's how I'm initializing Prophecy:

$prophet = new Prophet();
$out = $prophet->prophesize();
$out->willExtend(Outgoing::class);
$out->willBeConstructedWith(
  [
    $this->configStub,
    $this->loggerStub,
    $this->dbStub,
    'outgoing',
  ]
);

That all seems to work well.

But when I try to mock (is that the right terminology?) a method, I get an error when running the test.

Here I'm trying to make the connect() and push() methods of the Outgoing object return true. Then I call the method I'm testing. I used the PHPUnit setUp() method to initialize the stub and item earlier.

$out->connect([Argument::any()])->willReturn(true);
$out->push([Argument::type('array')])->shouldBeCalled()->willReturn(
  true
);
$result = $this->incoming->sendToOutgoing($this->item,$this->fieldStub);
$this->assertTrue($result);

My output from running the test:

Testing started at 8:18 PM ...
PHPUnit 9.5.21 #StandWithUkraine

TypeError : call_user_func(): Argument #1 ($callback) must be a valid callback, no array or string given
 /path/to/project/src/Queue/Queue.php:41
 /path/to/project/tests/SystemQueue/IncomingTest.php:274

Time: 00:00.150, Memory: 8.00 MB

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.
Process finished with exit code 2

Queue.php:41 is just setting the "config" parameter to the passed in Config object (in this case the stub.) The Outgoing class extends the Queue class.

IncomingTest.php:274 is my first method mock $out->connect(....

I also tried setting connect() and push() without any arguments, and without the array around the arguments. The same error resulted all three ways.

I'm running this on php 8.1.7 on PopOS 20.04.

Thanks in advance for any help!

stof commented 5 months ago

Without seeing the code, it is hard to answer.

In the method I'm testing, that class is called via new, so I can't just use PHPUnit's stubs/mocks and pass the object into the the method.

Prophecy should let me, essentially, intercept that class and replace it with a mock. Right?

no. Prophecy won't replace the class (which is impossible in PHP). Prophecy has a different user-facing API than PHPunit mocks, but it essentially work the same. Prophecy won't be able to replace objects instantiated with new in your code.