Summer Sale! All accounts are 50% off this week.

GrantMeFood's avatar

Mocked Eloquent model returns null when attempting to query for data

Howdy folks, relatively new to unit testing in Laravel. I'm trying to mock an eloquent model using the following pattern mentioned in the docs for 11.x, but the mock doesnt seem to be getting injected properly:

   	$storePhoneNumber = Mockery::mock();
	$storePhoneNumber->twilio_phone_number_sid = self::$sid;
    $storePhoneNumber->notification_email = self::$testEmail;
    $storePhoneNumber->phone_number = self::$testPhoneNumber;

    $this->storePhoneNumber->shouldReceive('where')
      ->with('phone_number', self::$testPhoneNumber)
      ->once()
      ->andReturn(Mockery::self())
      ->shouldReceive('first')
      ->once()
      ->andReturn($storePhoneNumber);

    $this->instance(StorePhoneNumber::class, $this->storePhoneNumber);

In the class I'm attempting to fetch a record from the StorePhoneNumber model inside of a method, but the model isnt getting mocked and has no records, so it returns null instead of returning the object I'm passing to andReturn().

What am I missing here? I want to avoid using the database here as much as possible, as I've managed to mock all of the other external services I'm using (mostly Twilio).

0 likes
6 replies
vincent15000's avatar

Perhaps the problem is just this.

   	$storePhoneNumber = Mockery::mock();
	$storePhoneNumber->twilio_phone_number_sid = self::$sid;
    $storePhoneNumber->notification_email = self::$testEmail;
    $storePhoneNumber->phone_number = self::$testPhoneNumber;

    $storePhoneNumber->shouldReceive('where')
    $storePhoneNumber->shouldReceive('where')

instead of

    $this->storePhoneNumber->shouldReceive('where')
GrantMeFood's avatar

Nah, I tried both approaches. I'm instantiating an instance of $storePhoneNumber on the class itself as a means of reusing the mock in subsequent tests.

1 like
GrantMeFood's avatar

I think the problem may be that the helper class that I'm testing isnt part of Laravel's service container, so it doesnt implicitly get the new mocked versions of the models. I may need to either inject these model classes from the controller or find a way to include them in the service container.

1 like
GrantMeFood's avatar

Just to close the loop here for folks running into this in the future: my previous comment about service containers was exactly the problem. In my helper class, I'm instantiating my models using the make() method from the App's service container:

$storePhoneNumber = App::make(StorePhoneNumber::class);
$sp = $storePhoneNumber.where('someField', 'someValue'); // this is now using the mock in my tests instead of using the native implementation

Edit: also, make sure you use $this->app->instance to bind the mocked Model class instead of just $this->instance, as the latter doesnt refer to the service container itself at the app level.

1 like

Please or to participate in this conversation.