Search code examples
phplaravellaravel-5

Column Update in Unit Test Not Reflected


The following test fails :

/** @test */
public function set_shift_on_given_availability_to_a_candidate()
{
    $candidate = $this->createCandidateWithFixedAvailability(pow(2, 3) - 1); 
    $candidate->availabilities[0]->setShift(App\Availability::SHIFT_TYPES['AM']);
    $candidateShifts = $candidate->availabilities[0]->shifts;

    $this->assertEquals(count($candidateShifts), 1);
    $this->assertTrue(in_array('AM', $candidateShifts));
}

When I try using the methods with artisan tinker I get the expected results:

>>> $candidate->availabilities()->first()->shifts();
=> [
     "AM",
   ]
>>> $candidate->availabilities()->first()->setShift(App\Availability::SHIFT_TYPES['PM']);
=> null
>>> $candidate->availabilities()->first()->shifts();
=> [
     "PM",
   ]

Implementation details of methods used :

public function setShift(...$shiftTypes)
{
    \DB::table($this->table)
        ->where([
            ['candidate_id', '=',  $this->candidate_id],
            ['day_of_week_id', '=', $this->day_of_week_id]
        ])
        ->update(['shifts' => array_sum ($shiftTypes)]);
}

public function shifts()
{
    $namedShifts = [];
    foreach (Availability::SHIFT_TYPES as $key => $val) {
        if($this->shifts & $val) {
            array_push($namedShifts, $key);
        }
    }
    return $namedShifts;
}

I am using a separate test database with the DatabaseTransactions trait. I don't understand why the update is not changing the column values when I attempt to get the columns value with shifts() in my unit test while it works with tinker.

From what I can see, using the DB query builder to update the column then fetching the dynamic property of that column does not reflect the update change, so either I fetch it using the query builder or I assign the shift value to the dynamic property in the setShift function when an update runs successfully. It's just odd that the dynamic property is correctly updated in tinker, but not in phpunit.


Solution

  • The main difference between your testing setup and the Tinker example is the usage of $candidate->availabilities()->first() vs $candidate->availabilities[0].

    $candidate->availabilities() is probably set up to return a HasMany relation. Calling $candidate->availabilities()->first() will thus execute an SQL statement directly on the database.

    $candidate->availabilities[0] resolves the availabilities relation. This can have "side effects" e.g. because the relation is already set up so that no "fresh" call to the DB is triggered. I guess that $this->createCandidateWithFixedAvailability(pow(2, 3) - 1); does not work as intended. Maybe you are creating the availabilities without actually saving them correctly so that certain properties are not set. A common error in this scenario is missing relation values (e.g. the candidate_id property in the availability entity isn't set).

    Now, this is all pretty speculative and purely based on the errors I found myself running into in the past ;)