【问题标题】:Save variable during unit testing Laravel在单元测试 Laravel 期间保存变量
【发布时间】:2019-08-10 21:27:41
【问题描述】:

变量$this->id在另一个函数中不可见testExemple

如果我将这个变量传递给一个不是从“测试”开始的普通函数而不是一个测试函数,那么一切都会正常工作。

我能以某种方式解决这个问题吗?

class LoginTest extends TestCase
{
    protected $id;
    public function testLogin()
    {
        $response = $this->json('post', 'auth/login', 
            ['email' => 'admin@mail.com', '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);
    }
}

【问题讨论】:

  • 这个用例是什么?您的单元测试应该是独立的,并且不应该需要以前测试的状态
  • 可以添加setUp方法,每次测试前都会运行:phpunit.de/manual/6.5/en/fixtures.html

标签: php laravel unit-testing phpunit


【解决方案1】:

据我所知,每个测试都是独立运行的,如果您想将数据从一个测试传递到另一个测试,您可以使用@depends doc 注释,如下所示:

class LoginTest extends TestCase
{
    public function testLogin()
    {
        $response = $this->json('post', 'auth/login', 
            ['email' => 'admin@mail.com', '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);
    }
}

但是您可能会遇到的问题是,虽然 $id 有一个值,但在此测试期间用户实际上并未登录,因为其他所有内容(例如会话)都将被清除干净。

为确保用户已登录,您需要模拟用户登录,如下所示:

    public function testExample()
    {
        $this->actingAs(User::where('email', 'admin@mail.com')->first()); //User now logged in
        echo(\Auth::id());
    }

这样可以确保用户登录并解耦测试。

【讨论】:

    【解决方案2】:

    之所以这样工作,是因为单元测试应该是独立的。一个测试设置的变量不应该被下一个测试访问。

    如果您的问题是您需要测试需要登录的内容,一个好的解决方案是创建一个扩展 TestCase 的新类并实现诸如 loginUser() 之类的辅助函数(它可能返回一个 User 实例)。

    然后您应该让您的测试扩展这个新类,而不是直接扩展 TestCase。

    每次运行需要您登录的测试时,您只需写 $this->loginUser() 并继续进行真正的测试。

    如果一个类中的所有测试都需要您登录,您甚至可以添加一个 setUp() 函数,该函数将在执行任何测试之前运行(记得同时调用 parrent::setUp()

    protected function setUp() {
      parent::setUp();
    
      $this->loginUser();
    }
    

    【讨论】:

    • 我需要在一项测试中为另一项测试采用特定变量
    • 你可以使用@depends,但是如果你依赖于其他测试,你所做的并不是真正的单元测试。见the other answer
    • @apokryfos 我不同意。如果一个测试不能独立运行,它就不是真正的单元测试。单元测试不应依赖任何外部状态。我仍然赞成您的解决方案,因为它解决了 OP 的问题,并且仍然有用于取决于其他测试的用途 - 但在这种情况下,我们不再谈论 单元测试
    【解决方案3】:

    假设您想将 $number 从 test1 传递到 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
    
        }
    
    }
    

    解决方案可能不是最好的,但至少它有效。嘿,我们是在做测试,而不是编写生产代码,所以谁在乎呢?

    【讨论】:

      猜你喜欢
      • 2020-08-24
      • 2020-03-11
      • 2018-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多