3

I'm writing unit tests for my laravel 5 application. In one test, I call a function and need to verify that it the static create method for another model is called in the function under test.

I don't want to actually persist any resources to the database.

I've created a mock object, specified expectations, and bound it to the app instance, but when create is called the application attempts to run SQL rather than having the function call intercepted by the mock object.

public function testLogCreation() {
  $this->log = Mockery::mock('App\Models\Log');
  $this->log->shouldReceive('create')->once();
  $this->app->instance('App\Models\Log',$this->log);

  echo get_class($this->app['App\Models\Log']); // output: Mockery_2_App_Models_Log
}

There was 1 error

SQLSTATE[23503]: Foreign key violation: 7 ERROR: insert or update on table "logs" violates foreign key constraint....

I also tried $this->log->shouldReceive('save')->once(); since I observed that the static create function calls the public save function, but I think I'm not creating the mock expectation on the right instance of log.

If this can't be accomplished, any suggestions for alternative strategies?

Thanks!

2 Answers2

1

Static methods are notoriously hard to cope with in unit tests. The line

$this->app->instance('App\Models\Log', $this->log);

installs your mock in the application for dependency injection, but dependency injection only comes into play with objects created by Laravel. Static methods do not actually have an underlying object so it doesn't apply.

One approach you can use is a factory or service interface which has a createLog method. This interface then has a concrete class which creates Log models using the static method (or better yet, removes the need for a static method). You can then easily mock that interface in your tests and validate that createLog is called.

See here for a great answer to a similar question: Laravel Dependency Injection: When do you have to? When can you mock Facades? Advantages of either method?

Community
  • 1
  • 1
bernie
  • 9,820
  • 5
  • 62
  • 92
0

You might be able to get around the issue by instantiating an Eloquent model via the service container and running save() on it in your production code (rather than using the create() method).

$log = $this->app->make('App\Models\Log')->fill($array);
$log->save();

This permits you to mock an actual object instead of a facade or static method. Then you can use Mockery like in your example, only mock the 'fill' and 'save' methods.

Fancypants_MD
  • 665
  • 1
  • 8
  • 8