3

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);
    }
}
2
  • What is the use-case for this? Your unit tests should be independent and should not require state from previous tests Commented Mar 20, 2019 at 8:37
  • You can add the setUp method, it will run before each test: phpunit.de/manual/6.5/en/fixtures.html Commented Mar 20, 2019 at 8:38

3 Answers 3

8

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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();
}

4 Comments

I need specific variable taken in one test for another test
You can use @depends then, but what you are doing is not really unit testing if you're depending on other tests. See the other answer
@apokryfos I disagree. If a test can not run independently it is not really a unit test. A unit test should not rely on any external state. I still upvoted your solution because it solves the OP's question and there are still uses for depending on other tests - but in that case we are no longer talking unit testing
Nope, I believe it should not be always independed. What if you are testing a workflow? or on API testing? do you tend to recreate all those steps each test until you reach?
1

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?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.