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

jrdavidson's avatar

Auth Testing

Thoughts on what I'm doing in this test class.

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class AuthTest extends TestCase
{
    use DatabaseTransactions;

    protected $user;
    protected $password = 'testpass123';

    public function get_user()
    {
        if ($this->user) return;

        $this->user = factory(App\User::class)->create([
            'email' => 'john@example.com',
            'password' => bcrypt($this->password),
        ]);
    }

    /** @test */
    public function a_user_can_successfully_log_in()
    {
        $this->get_user();
        $this->visit(route('login'));
        $this->type($this->user->email, 'email');
        $this->type($this->password, 'password');
        $this->press('Login');
        $this->seePageIs(route('dashboard'));
    }

    /** @test */
    public function a_user_receives_errors_for_wrong_login_credentials()
    {
        $this->visit(route('login'));
        $this->type($this->user->email, 'email');
        $this->type('invalid-password', 'password');
        $this->press('Login');
        $this->see('These credentials do not match our records.');
    }

    /** @test */
    public function a_user_is_redirected_to_dashboard_if_logged_in_and_tries_to_access_login_page()
    {
        $this->get_user();

        $this->actingAs($this->user);

        $this->visit(route('login'));
        $this->seePageIs(route('dashboard'));
    }

    /** @test */
    public function a_user_is_redirected_to_login_page_if_not_logged_in_and_trying_to_access_dashboard()
    {
        $this->get_user();
        $this->visit(route('dashboard'));
        $this->seePageIs(route('login'));
    }
}

0 likes
53 replies
ifpingram's avatar

What sort of thoughts are you looking for? Here are my initial ones, without looking into the details of each test;

  • code seems cleanly written.
  • some people may chain the testing functions as opposed to calling them from $this on each line.
  • test names seem nice an descriptive

As to whether the above provides you any value is difficult to say. It's only really testing boilerplate code and no real business logic. However, it seems like a neat start.

1 like
jrdavidson's avatar

@ifpingram or anyone else. When I run the test I get the following.

PHPUnit 4.8.21 by Sebastian Bergmann and contributors.

.EFF

Time: 756 ms, Memory: 18.25Mb

There was 1 error:

1) AuthTest::a_user_receives_errors_for_wrong_login_credentials
ErrorException: Trying to get property of non-object

/Users/me/Projects/app/tests/unit/AuthTest.php:39

--

There were 2 failures:

1) AuthTest::a_user_is_redirected_to_dashboard_if_logged_in_and_tries_to_access_login_page
Did not land on expected page [http://localhost/app/dashboard].

Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'http://localhost/app/dashboard'
+'http://localhost'

/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithPages.php:142
/Users/me/Projects/app/tests/unit/AuthTest.php:53

2) AuthTest::a_user_is_redirected_to_login_page_if_not_logged_in_and_trying_to_access_dashboard
Did not land on expected page [http://localhost/app/login].

Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'http://localhost/app/login'
+'http://localhost/app/dashboard'

/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithPages.php:142
/Users/me/Projects/app/tests/unit/AuthTest.php:61

FAILURES!
Tests: 4, Assertions: 13, Errors: 1, Failures: 2
2 likes
nfauchelle's avatar

@xtremer360

Well.. a_user_receives_errors_for_wrong_login_credentials is not calling $this->get_user();... but it's trying to reference it with $this->user->email

I would change the top get_user function from


    public function get_user()
    {
        if ($this->user) return;

        $this->user = factory(App\User::class)->create([
            'email' => 'john@example.com',
            'password' => bcrypt($this->password),
        ]);
    }

to

    public function setUp()
    {
        parent::setup();

        $this->user = factory(App\User::class)->create([
            'email' => 'john@example.com',
            'password' => bcrypt($this->password),
        ]);
    }

and then remove all the $this->get_user() in the tests.

1 like
skliche's avatar

@xtremer360 $this->user is null in a_user_receives_errors_for_wrong_login_credentials so you are seeing the Trying to get property of non-object error. It's value set in the test a_user_can_successfully_log_in before is not preserved when a test ends.

There are a couple options, e.g.

  • Call $this->get_user(); at the beginning of any test where you need a user. But keep in mind that it won't be the same user object. I would not recommend doing this.
  • Use a method annotated with /** @before */ to set up a user, see fixtures.
  • If you really need the same user object in multiple tests you can use test dependencies.
1 like
jrdavidson's avatar

@skliche Thanks for the reply. It doesn't have to be the same user for all the tests. So which of the first two do you think would be a better option for this scenario?

@nfauchelle If I make an attempt at your way then all of my tests time out at 2.01 minutes.

skliche's avatar

@xtremer360 If you only need the user object in a few of the tests within that test class use $this->get_user();. It's simple, easy and does the job - but you have to keep in mind to call it.

If you need a user object in all/almost all of the tests within that test class, go with the annotated method:

/** @before */
public function setupUserObjectBeforeAnyTest() {
    $this->user = factory(App\User::class)->create([
            'email' => 'john@example.com',
            'password' => bcrypt($this->password),
        ]);
}

But I'd recommend trying the annotated method anyway. It's something you most probably need for more complex tests in the future.

1 like
jrdavidson's avatar

@skliche What's the difference between doing that annotated way and doing the public function setUp in the test class?

bobbybouwmann's avatar

@xtremer360 This was you can declare multiple functions that will be ran before any other test. Let's say you need to create a lot of objects before you can test, you can imagine that your setUp method would be really big. This way you don't have to do that ;)

1 like
jrdavidson's avatar

@bobbybouwmann Thanks for clarifying that for me. However if I run my unit testsuite I get 4 errors and they are all because the operation timed out after 2.01 minutes still.

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class AuthTest extends TestCase
{
    use DatabaseTransactions;

    protected $user;
    protected $password = 'testpass123';

    /** @before */
    public function setupUserObjectBeforeAnyTest()
    {
        $this->user = factory(App\User::class)->create([
            'email' => 'john@example.com',
            'password' => bcrypt($this->password),
        ]);
    }

    /** @test */
    public function a_user_can_successfully_log_in()
    {
        $this->visit(route('login'));
        $this->type($this->user->email, 'email');
        $this->type($this->password, 'password');
        $this->press('Login');
        $this->seePageIs(route('dashboard'));
    }

    /** @test */
    public function a_user_receives_errors_for_wrong_login_credentials()
    {
        $this->visit(route('login'));
        $this->type($this->user->email, 'email');
        $this->type('invalid-password', 'password');
        $this->press('Login');
        $this->see('These credentials do not match our records.');
    }

    /** @test */
    public function a_user_is_redirected_to_dashboard_if_logged_in_and_tries_to_access_login_page()
    {
        $this->actingAs($this->user);

        $this->visit(route('login'));
        $this->seePageIs(route('dashboard'));
    }

    /** @test */
    public function a_user_is_redirected_to_login_page_if_not_logged_in_and_trying_to_access_dashboard()
    {
        $this->visit(route('dashboard'));
        $this->seePageIs(route('login'));
    }
}
bobbybouwmann's avatar

Are you sure your database and stuff is working and setup correctly? The tests seem fine to me..

jrdavidson's avatar

Yes. I've ran tests before I started doing these changes and it works fine.

jdrzejb's avatar

I tried to run these tests and this one

  /** @test */
    public function a_user_is_redirected_to_dashboard_if_logged_in_and_tries_to_access_login_page()
    {
        $this->actingAs($this->user);

        $this->visit(route('login'));
        $this->seePageIs(route('dashboard'));
    }

is not running. When I commented it out, all other tests are passing

skliche's avatar

@xtremer360 Run the tests with phpunit --debug. It should output what tests are run in what sequence and you should be able to see what test is causing the lockup. Additionally you could call each test separately like phpunit --filter name_of_test_method.

jrdavidson's avatar

@skliche I performed the suggested commands by running phpunit --debug and saw the results and it gave me the same error that I received before with the connection timing out.

@bobbybouwmann Would you know why?

skliche's avatar

@xtremer360 Show us the exact error you get. Do you get that error even when you run just a single test using --filter?

jrdavidson's avatar

@skliche This is what is returned when I run phpunit.

Me-iMac:backstage me$ phpunit
PHPUnit 4.8.21 by Sebastian Bergmann and contributors.

EEEE

Time: 2.01 minutes, Memory: 14.25Mb

There were 4 errors:

1) AuthTest::a_user_can_successfully_log_in
PDOException: SQLSTATE[HY000] [2002] Operation timed out

/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:55
/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php:22
/Users/me/Projects/app/bootstrap/cache/compiled.php:11423
/Users/me/Projects/app/bootstrap/cache/compiled.php:11419
/Users/me/Projects/app/bootstrap/cache/compiled.php:11326
/Users/me/Projects/app/bootstrap/cache/compiled.php:11281
/Users/me/Projects/app/bootstrap/cache/compiled.php:11157
/Users/me/Projects/app/bootstrap/cache/compiled.php:11144
/Users/me/Projects/app/bootstrap/cache/compiled.php:10568
/Users/me/Projects/app/bootstrap/cache/compiled.php:10559
/Users/me/Projects/app/bootstrap/cache/compiled.php:10391
/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:86
/Users/me/Projects/app/tests/unit/AuthTest.php:18

2) AuthTest::a_user_receives_errors_for_wrong_login_credentials
PDOException: SQLSTATE[HY000] [2002] Operation timed out

/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:55
/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php:22
/Users/me/Projects/app/bootstrap/cache/compiled.php:11423
/Users/me/Projects/app/bootstrap/cache/compiled.php:11419
/Users/me/Projects/app/bootstrap/cache/compiled.php:11326
/Users/me/Projects/app/bootstrap/cache/compiled.php:11281
/Users/me/Projects/app/bootstrap/cache/compiled.php:11157
/Users/me/Projects/app/bootstrap/cache/compiled.php:11144
/Users/me/Projects/app/bootstrap/cache/compiled.php:10568
/Users/me/Projects/app/bootstrap/cache/compiled.php:10559
/Users/me/Projects/app/bootstrap/cache/compiled.php:10391
/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:86
/Users/me/Projects/app/tests/unit/AuthTest.php:18

3) AuthTest::a_user_is_redirected_to_dashboard_if_logged_in_and_tries_to_access_login_page
PDOException: SQLSTATE[HY000] [2002] Operation timed out

/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:55
/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php:22
/Users/me/Projects/app/bootstrap/cache/compiled.php:11423
/Users/me/Projects/app/bootstrap/cache/compiled.php:11419
/Users/me/Projects/app/bootstrap/cache/compiled.php:11326
/Users/me/Projects/app/bootstrap/cache/compiled.php:11281
/Users/me/Projects/app/bootstrap/cache/compiled.php:11157
/Users/me/Projects/app/bootstrap/cache/compiled.php:11144
/Users/me/Projects/app/bootstrap/cache/compiled.php:10568
/Users/me/Projects/app/bootstrap/cache/compiled.php:10559
/Users/me/Projects/app/bootstrap/cache/compiled.php:10391
/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:86
/Users/me/Projects/app/tests/unit/AuthTest.php:18

4) AuthTest::a_user_is_redirected_to_login_page_if_not_logged_in_and_trying_to_access_dashboard
PDOException: SQLSTATE[HY000] [2002] Operation timed out

/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:55
/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php:22
/Users/me/Projects/app/bootstrap/cache/compiled.php:11423
/Users/me/Projects/app/bootstrap/cache/compiled.php:11419
/Users/me/Projects/app/bootstrap/cache/compiled.php:11326
/Users/me/Projects/app/bootstrap/cache/compiled.php:11281
/Users/me/Projects/app/bootstrap/cache/compiled.php:11157
/Users/me/Projects/app/bootstrap/cache/compiled.php:11144
/Users/me/Projects/app/bootstrap/cache/compiled.php:10568
/Users/me/Projects/app/bootstrap/cache/compiled.php:10559
/Users/me/Projects/app/bootstrap/cache/compiled.php:10391
/Users/me/Projects/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:86
/Users/me/Projects/app/tests/unit/AuthTest.php:18

FAILURES!
Tests: 4, Assertions: 0, Errors: 4.
JillzTom's avatar

@xtremer360 Check if your database connection variables are correct and the mysql or whatever it is up and running. Especially check the host and unix_socket

jrdavidson's avatar

I do. Let me show you what that looks like.

'mysql_testing' => [
            'driver'    => 'mysql',
            'host'      => env('TESTING_DB_HOST', 'localhost'),
            'database'  => env('TESTING_DB_DATABASE', 'forge'),
            'username'  => env('TESTING_DB_USERNAME', 'forge'),
            'password'  => env('TESTING_DB_PASSWORD', ''),
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
            'strict'    => false,
        ],
TESTING_DB_HOST=192.168.10.10
TESTING_DB_DATABASE=app_testing
TESTING_DB_USERNAME=homestead
TESTING_DB_PASSWORD=secret
JillzTom's avatar

Is your host IP address correct? Also use this:

unix_socket = /var/run/mysqld/mysqld.sock
jrdavidson's avatar

Why do I need to use that. I've never seen people use that?

JillzTom's avatar

Sometimes, MySQL needs to be specified about the socket being used for the connection. Are you using MAMP on Mac?

jrdavidson's avatar

I do not because Laracasts has taught me to use homestead and not use Mamp.

skliche's avatar

@xtremer360 Does the problem still exist if you reduce the test to the following?

class ExampleTest extends TestCase {

    protected $user;
    protected $password = "test";


    /** @before */
    public function setupUserObjectBeforeAnyTest() {
        // need to set up database transactions support manually first, otherwise the user will be created before database transactions are set up by the usual trait
        $this->app->make('db')->beginTransaction();
        $this->beforeApplicationDestroyed(function () {
            $this->app->make('db')->rollBack();
        });

        $this->user = factory(App\User::class)->create([
            'email' => 'john@example.com',
            'password' => bcrypt($this->password),
        ]);
    }

    public function a_user_is_redirected_to_dashboard_if_logged_in_and_tries_to_access_login_page()  {
        $this->actingAs($this->user);

        $this->visit(route('login'));
        $this->seePageIs(route('dashboard'));
    }
}

That works for me. If this still runs into a timeout on your system then something's definitely wrong with your database connection.

1 like
jrdavidson's avatar

If I create that code snippet as a new test file and put it inside of my unit test folder as the only test file and do the following it shows what I receive.

Me-iMac:app me$ phpunit --testsuite unit
PHPUnit 4.8.21 by Sebastian Bergmann and contributors.

F

Time: 76 ms, Memory: 10.50Mb

There was 1 failure:

1) Warning
No tests found in class "ExampleTest".

FAILURES!
Tests: 1, Assertions: 0, Failures: 1.
Next

Please or to participate in this conversation.