Dmitry
Dmitry

Reputation: 59

Save variable during unit testing Laravel

Variable $this->id not visible in another function testExemple

If I pass this variable to a normal function that does not start on a “test” and not to a testing function, everything will work.

Can I fix this somehow?

class LoginTest extends TestCase
{
    protected $id;
    public function testLogin()
    {
        $response = $this->json('post', 'auth/login', 
            ['email' => '[email protected]', 'password' => '12345678'])
            ->assertJsonStructure(['data' => ['id', 'name', 'email']]);

        $response->assertStatus(201);

        $userData = $response->getContent();
        $userData = json_decode($userData, true);
        $this->id = $userData['data']['id'];
    }
    public function testExemple()
    {
        echo($this->id);
    }
}

Upvotes: 3

Views: 2518

Answers (3)

Berni
Berni

Reputation: 568

Lets suppose you want to pass $number from test1 to test2 :

class Test extends TestCase

{

    public function test1()

    {

        $number = 1;
        // store the variable, only available during testsuite execution, 
        // it does not necessarily have to exist in your .env file .

        putenv('MY_NUMBER=' . $number);

        //success
        $this->assertEquals(getenv('MY_NUMBER'), $number);
    }

    public function test2()
    {

        //hurray ! variable retrieved
        $this->assertEquals(1, getenv('MY_NUMBER'));

        // after tearing down , MY_NUMBER is cleared

    }

}

The solution may not be the best, but at least it works. And hey, we are doing testing, not writting production code, so who cares?

Upvotes: 1

apokryfos
apokryfos

Reputation: 40690

Each test runs independently as far as I know, if you want to pass data from one test to another you can use the @depends doc comment like below:

class LoginTest extends TestCase
{
    public function testLogin()
    {
        $response = $this->json('post', 'auth/login', 
            ['email' => '[email protected]', 'password' => '12345678'])
            ->assertJsonStructure(['data' => ['id', 'name', 'email']]);

        $response->assertStatus(201);

        $userData = $response->getContent();
        $userData = json_decode($userData, true);
        return $userData['data']['id']; //Return this for the dependent tests
    }

    /**
      * @depends testLogin
      */
    public function testExample($id)
    {
        echo($id);
    }
}

However the problem you might encounter is that while the $id has a value the user is not actually logged in during this test because everything else (e.g. session) will be wiped clean.

To ensure the user is logged in then you will need to mock user login like below:

    public function testExample()
    {
        $this->actingAs(User::where('email', '[email protected]')->first()); //User now logged in
        echo(\Auth::id());
    }

This ensures the user is logged in and also decouples tests.

Upvotes: 8

Daniel
Daniel

Reputation: 11182

It works like this because unit tests should be indepedent. A variable set by one test should never be accessible for the next.

If your issue is that you need to test things that requires you to be logged in, a good solution is creating a new class that extends TestCase and implementing helper functions such as loginUser() (which could return a User instance).

You should then have your tests extend this new class instead of directly extending TestCase.

Every time you run a test that requires you to log in, you can just write $this->loginUser() and go ahead with your real test.

If all tests in a class requires you to log in, you can even add a setUp() function that will run right before any test is executed (remember to also call parrent::setUp():

protected function setUp() {
  parent::setUp();

  $this->loginUser();
}

Upvotes: 2

Related Questions