Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

normykinz's avatar

Bizarre Behavior in Unit Test

This has really got me scratching my head. I have two unit tests ...

test('token is valid if valid_until is null', function () {
    $token = new UserToken();
    $token->value = Str::random(32);
    $token->type = TokenType::VERIFICATION;
    $token->valid_until = null;

    expect($token->isValid())->toEqual(true);
});

test('token is valid if valid_until is later than the current date and time', function () {
    $token = new UserToken();
    $token->value = Str::random(32);
    $token->type = TokenType::VERIFICATION;
    $token->valid_until = now()->addDays(1);

    expect($token->isValid())->toEqual(true);
});

The reason I'm not using a factory is because I have custom faker providers registered. So unit tests throw a fit!

I get the following test output

   FAIL  Tests\Unit\UserTokenTest
  ✓ token is valid if valid_until is null
  ⨯ token is valid if valid_until is later than the current date and time

  ---

  • Tests\Unit\UserTokenTest > token is valid if valid_until is later than the current date and time
   PHPUnit\Framework\ExceptionWrapper

  Call to a member function connection() on null

  at D:\workspace\PHP\iximoo\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php:1820
    1816▕      * @return \Illuminate\Database\Connection
    1817▕      */
    1818▕     public static function resolveConnection($connection = null)
    1819▕     {
  ➜ 1820▕         return static::$resolver->connection($connection);
    1821▕     }
    1822▕
    1823▕     /**
    1824▕      * Get the connection resolver instance.

  1   D:\workspace\PHP\iximoo\vendor\bin\pest:120
      include("D:\workspace\PHP\iximoo\vendor\pestphp\pest\bin\pest")


  Tests:  1 failed, 1 passed
  Time:   0.06s

So, why does Carbon need to establish a database connection?

0 likes
1 reply
LaryAI's avatar
Level 58

The issue seems to be related to the fact that the now() function in the second test is using Carbon, which in turn is trying to resolve a database connection. One possible solution is to mock the now() function to return a fixed date and time, instead of relying on Carbon to calculate it dynamically. This can be achieved using the Carbon::setTestNow() method.

Here's an updated version of the second test that uses this approach:

use Carbon\Carbon;

test('token is valid if valid_until is later than the current date and time', function () {
    $token = new UserToken();
    $token->value = Str::random(32);
    $token->type = TokenType::VERIFICATION;
    $token->valid_until = Carbon::now()->addDays(1);

    Carbon::setTestNow($token->valid_until->subSeconds(1)); // set "now" to one second before valid_until

    expect($token->isValid())->toEqual(false);

    Carbon::setTestNow($token->valid_until); // set "now" to valid_until

    expect($token->isValid())->toEqual(true);

    Carbon::setTestNow(); // reset "now" to the real current date and time
});

This test sets the "now" time to one second before the valid_until time, and checks that the token is not valid at that point. Then it sets the "now" time to the valid_until time, and checks that the token is valid at that point. Finally, it resets the "now" time to the real current date and time.

Note that this approach requires the Carbon class to be imported at the top of the test file:

use Carbon\Carbon;

Please or to participate in this conversation.