Mockery\Exception\NoMatchingExpectationException PHPUnit test fails - unit-testing

Im testing with PHPUnit and my test fails on a function. But i don't know why.
The function i want to mock:
public function subscribe($email)
{
$message = new SubscribeMessage();
$message->setEmailaddress($email);
$message->setLocale(Locale::getDefault());
$this->getAmqpProducer()->publish($message, 'newsletter-subscribe');
return true;
}
and my Unit test:
public function testSubscribeSendsAmqpMessage()
{
$email = 'email#email.nl';
$locale = 'nl';
$this->amqpProducerMock
->shouldReceive('publish')
->once()
->with(
\Mockery::on(
function ($message, $routingkey) use (&$publishedMessage) {
$publishedMessage = $message;
return $routingkey == 'newsletter-subscribe';
}
)
);
$this->service->subscribe($email, $locale);
}
but the test says:
Mockery\Exception\NoMatchingExpectationException : No matching handler found for AcsiRabbitMq\Producer\Producer::publish(AcsiNewsletter\RabbitMq\Message\SubscribeMessage, "newsletter-subscribe"). Either the method was unexpected or its arguments matched no expected argument list for this method
How can i fix my Unit test? Or how can i refactor my test?

You Mock the subscribe, not the internal publish. When you run the test and call ->subscribe, it will attempt to execute the code in the class. Therefore, it will try to run the subscribe() method, which you appear to have a strange reference to your Mock.
Normally, your test will mock the subscribe, so you can return a value for the assert test, which is hard coded.
You appear to have tried to mock the GetAmqpProducer() object that is in your regular code. You need to either be able to pass the mock object to be used into your class, or to be able to assign it.
Simplified Example:
class Email
{
private $MsgObject;
// Constructor Injection
public __construct(SubscribeMessage $MessageObject)
{
$this->MsgObject = $MessageObject;
...
}
// Setter Injection
public function SetSubscribeMessage(Subscribe $MessageObject)
{
$this->MsgObject = $MessageObject;
}
public function setEmailaddress($email)
{
$this->MsgObject->emailAddress = $email;
...
}
public function setLocale($Locale)
{
$this->MsgObject->Locale = $Locale;
...
}
...
}
Your class sample above has too many internal objects and dependencies to be tested as such, since the test will actually call these. You would use Dependency Injection to pass the objects with known state, and have them return properly.
Please note, I am not showing how to do this in Mockery, as I do not use it, but this simple example should help you understand what I am trying to express.
So a simple test might look like:
public function testSubscribeMessage()
{
$email = 'email#email.nl';
$this->Mock(
->shouldReceive('setEmailAddress')
->once()
->will_return($email)
);
$SubscribeMessage = new SubscribeMessage($this->Mock);
$SetEmail = $SubscribeMessage->setEmailAddress($email);
$this->assertEquals($email, $SetEmail);
}

Related

Phpunit, Dependency Injection, test stubs: inject mock, or anonymus object?

I have some doubt what to inject. Given this code:
class A
{
public function getSomething()
{
return 'something';
}
}
class TestMe
{
/**
* #var A
*/
private $a;
public function __construct($a)
{
$this->a = $a;
}
public function greetings()
{
return 'Hello, '.$this->a->getSomething();
}
}
my test A:
function testA()
{
$a = new class() {
public function getSomething()
{
return 'aAnonimus';
}
};
$sut = new TestMe($a);
$this->assertEquals($sut->greetings(), 'Hello, aAnonimus');
}
testB, same but with mock:
function testA()
{
$a = $this->createMock(A::class);
$a->method('getSomething')->willReturn('bMockery');
$sut = new TestMe($a);
$this->assertEquals($sut->greetings(), 'Hello, bMockery');
}
in the first test I simply inject a plain object.
But the second its more Phpunit's way: using mocked objects.
Question is, for long period which one wins? I find the first more conviement, and for the 2nd test, you have to know the class name of dependency (otherwise you cant create a mock)
In the longterm, it's better the second way because it's better to have type hint in the constructor which will not allow you to provide a simple object.
Also when we are talking about UnitTests you should test a certain class without depending on 3rd party libraries or other services logic. So the best way is to use mocks for all of the services which are part of the tested class

Testing against early returns (defensive programming)

I am learning testing and trying to test a function using 'early returns'. The function on success sets a property in another class and on failure simply returns so in both cases it 'returns' void.
class Test
{
private $fileHandler;
private $config;
public __constructor($fileHandler, $config)
{
$this->fileHandler = $fileHandler;
$this->config = $config;
}
public function example($filePath)
{
$exists = $this->fileHandler->exists($filePath);
if ($exists === false) {
return;
}
$this->config->set($filePath);
}
}
In this example I believe I can test this with two unit tests and by mocking the fileHandler class.
For a failure (early return) the $config class's method set() should not be called whilst for a success the method should be called.
However, this test passes if I try and change never() to once() making me think the entire test is bogus.
/** test */
public function config_is_not_set_with_missing_file()
{
$fileHandlerMock = $this->getMockBuilder(fileHandler::class)->getMock;
$fileHandlerMock->method('exists')
->willReturn('false');
$configMock = $this->getMockBuilder(config::class)->getMock;
$test = new Test($fileHandlerMock, $configMock);
$test->example('fake file path');
$configMock->expects($this->never())
->method('set');
}
Your file handler mock is returning the string 'false', which is !== to false. Change that to false and Tets::example should return early.
You're not passing the $configMock to the Test constructor, so it's not being used.
You're right, if the test passes both with once and never expectations, the test is not working as expected and requires reviewing it.

Mockery mock method inside closure

I have problem unit testing method inside closure called by call_user_func() example :
public function trans($lang, $callback)
{
$this->sitepress->switch_lang($lang);
call_user_func($callback);
}
on controller :
public function sendMail()
{
$foo = $baz = 'something';
$mail = $this->mailer;
$this->helper->trans_c('en', function() use($foo, $baz, $mail) {
$mail->send('Subject', $foo, $baz);
});
}
test case :
public function testSomething()
{
$helperMock = Mockery::mock('Acme\Helper');
$helperMock->shouldReceive('trans_c')->once(); // passed
$mailMock = Mockery::mock('Acme\Mail');
$mailMock->shouldReceive('send')->once(); // got should be called 1 times instead 0
$act = new SendMailController($helperMock, $mailMock);
$act->sendMail();
}
how can I ensure that ->send() method is called inside closure trans_c()
I tried with
$helperMock->shouldReceive('trans_c')->with('en', function() use($mailMock) {
$mailMock->shouldReceive('send');
});
no luck. :(
well it works fine with passing Mockery::type('Closure') in the second param of trans_c, but I really need to ensure that method send from mailer class is called.
A mocked class does not execute the real code by default. If you mock the helper it will check that the calls are being made but won't execute the anonymous function.
With mockery, you can configure the expectation so that the real method will be executed: passthru();
Try this:
$helperMock = Mockery::mock('Acme\Helper');
$helperMock
->shouldReceive('trans_c')
->once()
->passthru()
;
This is explained in the docs.
EDIT
Maybe you don't really need to mock the helper. If you mock the Mail class and expect the send method to be called once, just let the real helper do it.

Confused about PhpSpec stubs and mocks again

I'm building a Laravel 5 application at the moment and have gotten myself confused about how to mock things in PhpSpec.
I'm building a schedule times validator that requires the intended schedule to be checked against all current schedules and see if there's any overlap (events are not allowed to overlap).
I need to pull in the schedules in question so I can test against them. At the moment it's a very basic whereBetween query, but it's going to get a lot more complicated as there'll be recurring schedules to check against as well.
So here's my stripped down class. I really just want to test the doesNotOverlap function.
use App\Schedule;
class ScheduleTimesValidator
{
protected $schedule;
public function __construct(Schedule $schedule)
{
$this->schedule = $schedule;
}
public function doesNotOverlap($slug, $intended)
{
$schedules = $this->getSchedulesBetween($slug, $intended);
if(empty($schedules)) return true;
return false;
}
protected function getSchedulesBetween($slug, $intended)
{
// Casting to array to make testing a little easier
return $this->schedule->whereIsRecurring(false)
->ofChurch($slug)
->whereBetween('start', [$intended['start'], $intended['end']])
->get()->toArray();
}
and here's my Spec
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ScheduleTimesValidatorSpec extends ObjectBehavior
{
protected $validIntended = [
'start' => '2015-12-01 12:00:00',
'end' => '2015-12-01 13:00:00'
];
protected $churchNonRecurringSchedules = [
['start' => '2014-11-20 13:00:00', 'end' => '2014-11-21 14:00:00'],
['start' => '2014-11-23 10:36:07', 'end' => '2014-11-23 11:36:07'],
];
function let($schedule)
{
$schedule->beADoubleOf('App\Schedule');
$this->beConstructedWith($schedule);
}
function it_is_initializable()
{
$this->shouldHaveType('App\Validation\ScheduleTimesValidator');
}
function it_should_return_true_if_it_does_not_overlap($schedule)
{
// $schedule->any()->willReturn([]);
// $schedule->whereIsRecurring()->shouldBeCalled();
// $schedule->whereIsRecurring(false)->ofChurch()->whereBetween()->get()->toArray()->willReturn([]);
// $schedule->willReturn([]);
// $this->getSchedulesBetween('slug', $this->validIntended)->willReturn([]);
$this->doesNotOverlap('slug', $this->validIntended)->shouldReturn(true);
}
// Tear Down
function letgo() {}
}
If I run it like that I get:
! it should return true if it does not overlap
method 'Double\App\Schedule\P8::whereIsRecurring()' not found.
I tried (as you can see) various commented out things to mock what $schedule will return, but that doesn't seem to work.
So I guess I want to mock the protected getSchedulesBetween method in the class, but doing things like $this->getSchedulesBetween($arg, $arg)->willReturn(blah) doesn't work.
Do I need to pull getSchedulesBetween() out of the class and move it into another class and then mock that? Or do I need to push $this->schedule->blah into the doestNotOverlap method so I can mock what $schedule will return?
I don't want to actually test the App\Schedule Laravel Model - I just want to mock what it's returning and will be hardcoding a variety of queries that will be run to get the different model results.
End of a long day here so brain a little zonked.
Update 2014-10-23
So I created a scope on my Schedule model
public function scopeSchedulesBetween($query, $slug, $intended)
{
return $query->whereIsRecurring(false)
->ofChurch($slug)
->whereBetween('start', [$intended['start'], $intended['end']]);
}
Then created a new App\Helpers\ScheduleQueryHelper which instantiated App\Schedule as a variable and added this method:
public function getSchedulesBetween($slug, $intended)
{
return $this->schedule->schedulesBetween($slug, $intended)->get()->toArray();
}
Then updated my spec to do
function let($scheduleQueryHelper)
{
$scheduleQueryHelper->beADoubleOf('App\Helpers\ScheduleQueryHelper');
$this->beConstructedWith($scheduleQueryHelper);
}
function it_should_return_true_if_it_does_not_overlap($scheduleQueryHelper)
{
$scheduleQueryHelper->getSchedulesBetween('slug', $this->validIntended)->willReturn([]);
$this->doesNotOverlap('slug', $this->validIntended)->shouldReturn(true);
}
And back in my ScheduleTimesValidator class did
public function doesNotOverlap($slug, $intended)
{
$schedules = $this->scheduleQueryHelper->getSchedulesBetween($slug, $intended);
if(empty($schedules)) {
return true;
}
return false;
}
And now PhpSpec is mocking that other class ok. However this seems like a very roundabout way to be doing things.

PHPUnit Mock overrides existing methods

I'm writing a unit test for a REST Service connector which is using a third party tool called Httpful.
Because I do not want to send real requests to the server, I mocked the "send" method from Httpful\Request:
$mockedRequest = $this->getMock('Httpful\Request', array('send'), array(), '', false);
$mockedRequest->expects($this->once())->method('send');
This works fine, but the Request Class has a method called expects itself, which I use in my actual code to define the acceptable mime type of the response.
$this
->getRequest('GET')
->uri(ENDPOINT . $configurationId) //by default this returns a Request Object (now mocked request)
->expects('application/json') //crashes ...
->send();
When the code gets executed, I get the following error (which is understandable):
Argument 1 passed to Mock_Request_938fb981::expects() must implement interface PHPUnit_Framework_MockObject_Matcher_Invocation, string given
Is there something like a configurable prefix for methods coming from the Mock Class like "expects"?
I don't think that you will be able to do that using the PHPUnit_MockObject class. But you can code your own and use that instead.
class MockRequest extends \Httpful\Request {
public $isSendCalled = false;
public $isUriCalled = false;
public $isExpectsCalled = false;
public function uri($url) {
if($url !== '<expected uri>') {
throw new PHPUnit_Framework_AssertionFailedError($url . " is not correct");
}
$this->isUriCalled = true;
return $this;
}
public function expects($type) {
if($type !== 'application/json') {
throw new PHPUnit_Framework_AssertionFailedError($type . " is not correct");
}
$this->isExpectsCalled = true;
return $this;
}
public function send() {
$this->isSendCalled = true;
}
}
Your line for creating the mock then just becomes:
$mockedRequest = new MockRequest();
If the constructor fo
Then in your test you can verify that the methods are called with
$this->assertTrue($mockedRequest->isSendCalled);
$this->assertTrue($mockedRequest->isUriCalled);
$this->assertTrue($mockedRequest->isExpectsCalled);
This isn't a very dynamic mock but it will pass the type hinting and does your check for you. I would create this mock in the same file as your test (though be careful about not accidentally redefining this class elsewhere in your test suite). But it gets you around the problem of having expects being overridden.
The PHPUnit_Framework_MockObject_MockObject is an interface that sets the signature for expects() also which your class wouldn't meet and so there would be an error if you were able to rename the method.
https://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/src/Framework/MockObject/MockObject.php