normykinz's avatar

Odd problem testing using in-memory Sqlite database

Hello peeps. I hope you can help me solve this odd little problem.

I have PhpUnit configued to use an in-memory SqlLite database.

<env name="DB_CONNECTION" value="sqlite" />
<env name="DB_DATABASE" value=":memory:" />

I'm testing a custom artisan command which creates a super user that checks if an email already exists in the users table.

while (User::where('email', $email)->exists()) {
		$this->error('Email is alreay registered');
		$email = $this->askEmail();
}

But the test fails with this error.

Question "Super user's password" was not asked.

  at vendor/laravel/framework/src/Illuminate/Database/Connection.php:709
    705▕      */
    706▕     protected function run($query, $bindings, Closure $callback)
    707▕     {
    708▕         foreach ($this->beforeExecutingCallbacks as $beforeExecutingCallback) {
  ➜ 709▕             $beforeExecutingCallback($query, $bindings, $this);
    710▕         }
    711▕
    712▕         $this->reconnectIfMissingConnection();
    713▕

      +3 vendor frames
  4   app/Console/Commands/Veellas/SuperUser.php:54
      Illuminate\Database\Eloquent\Builder::__call()

  5   app/Console/Commands/Veellas/SuperUser.php:19
      App\Console\Commands\Veellas\SuperUser::askEmail()

Howver if I temporarely comment out the email check the user is stored in the database.

Can anybody shed any light on what moght ne going wrong?

0 likes
9 replies
normykinz's avatar

@sinnbeck Here we go. Both very simple ...

Test using Pest ...

it('it stores a super user in the database', function () {
    $this->artisan('xxxx:superuser')
        ->expectsQuestion("Super user's name", 'Super User')
        ->expectsQuestion("Super user's email", '[email protected]')
        ->expectsQuestion("Super user's password", 'password')
        ->expectsQuestion('Confirm password', 'password')
        ->assertExitCode(Command::SUCCESS);

    $this->assertDatabaseHas('users', [
        'name' => 'Super User',
        'email' => '[email protected]',
    ]);
});

And the askEmail method ...

    private function askEmail(): string
    {
        $email = $this->ask("Super user's email");

        if ($email == null) {
            $this->error('Email must not be empty');
            $email = $this->askEmail();
        }

        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->error('Email is not valid');
            $email = $this->askEmail();
        }

        while (User::where('email', $email)->exists()) {
            $this->error('Email is alreay registered');
            $email = $this->askEmail();
        }

        return $email;
    }
Sinnbeck's avatar

@normykinz Where does it ask for the password?

You have this assertion, but I dont see any code for actually asking it

        ->expectsQuestion("Super user's password", 'password')
normykinz's avatar

@Sinnbeck It asks for the password after the email.

The problem seems to be in the while loop while checking for an existing user with the same password.

Here's the whole command class ...


namespace App\Console\Commands\Veellas;

use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;

class SuperUser extends Command
{
    protected $signature = 'xxxx:superuser';

    protected $description = 'STores a super user in the database';

    public function handle()
    {
        $name = $this->askName();
        $email = $this->askEmail();
        $password = $this->askPassword();

        $this->storeUser($name, $email, $password);
        $this->info('Super user has been stored in the database');

        return Command::SUCCESS;
    }

    private function askName(): string
    {
        $name = $this->ask("Super user's name");

        if ($name == null) {
            $this->error('Name must not be empty');
            $name = $this->askName();
        }

        return $name;
    }

    private function askEmail(): string
    {
        $email = $this->ask("Super user's email");

        if ($email == null) {
            $this->error('Email must not be empty');
            $email = $this->askEmail();
        }

        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->error('Email is not valid');
            $email = $this->askEmail();
        }

        while (User::where('email', $email)->exists()) {
            $this->error('Email is alreay registered');
            $email = $this->askEmail();
        }

        return $email;
    }

    private function askPassword(): string
    {
        $password = $this->secret("Super user's password");
        if ($password == null) {
            $this->error('Password must not be empty');
            $password = $this->askPassword();
        }

        $confirmation = $this->secret('Confirm password');
        if ($confirmation == null) {
            $this->error('Password confirmation must not be empty');
            $confirmation = $this->secret('Confirm password');
        }

        if ($password != $confirmation) {
            $this->error('Password and confirmation do not match');
            $this->askPassword();
        }

        return $password;
    }

    private function storeUser(string $name, string $email, string $password): void
    {
        $user = new User();
        $user->name = $name;
        $user->email = $email;
        $user->password = Hash::make($password);
        $user->email_verified_at = Carbon::now();
        $user->save();
    }
}
AungHtetPaing__'s avatar

@normykinz What happen if you use if instead of while?

if (User::where('email', $email)->exists()) {
            $this->error('Email is alreay registered');
            $email = $this->askEmail();
        }
normykinz's avatar

I've found the problem.

I started using the LazilyReeshDatabase trait in the TestCase class.

When I switch back to ResfreshDatabase in the test, it started working.

I'll submit a bug report.

Thankyou peps.

Sinnbeck's avatar

@normykinz good to hear. Remember to make a reproduction of the bug in a github repo and attach a link to it. That will help getting it fixed :)

Please or to participate in this conversation.