Craig Ward
Craig Ward

Reputation: 2485

Laravel - Faking a save failure in unit tests

If we had the below code that we wanted to test, what would be the best way to simulate a failed save? At the moment the only part I am missing from my test is the else statement.

The save is part of a loop, where we take $customers loop through them and perform some actions.

$customers = Customer::where('created_at', '<=', $start);

$customers->each(function ($customer, $key) {
    if ($customer->save()) {
        //Do something here
    } else {
        //Saving failed, log something
    }
}

All the data for the tests come from factories, and are generated on the fly per test.

Upvotes: 0

Views: 1120

Answers (1)

apokryfos
apokryfos

Reputation: 40690

Well the easy but dirty thing to do is to fake a saving failiure through the saving event:

Here's the note from the event handler:

public function save(array $options = [])
{
    $query = $this->newModelQuery();

    // If the "saving" event returns false we'll bail out of the save and return
    // false, indicating that the save failed. This provides a chance for any
    // listeners to cancel save operations if validations fail or whatever.
    if ($this->fireModelEvent('saving') === false) {
        return false;
    }
   ....

Therefore something like the following should work:

class TestModelSaving {
     public function testSaveFailureLogs() {
          // Create the fake model here
          // If the event handler for saving returns false then `save()` will return false
          Customer::saving(function () { return false; }); 
          // Call your unit under test here

          // Cleanup: Usually unnecessary, but some test configurations might need it
          Customer::flushEventListeners();

     }
}

In order to test if things are logged you could mock the logger facade via Log::shouldReceive(....) (parameters are the same for the mockery function with the same name)

Upvotes: 1

Related Questions