Laravel 5.6单元测试从头到尾维护登录会话

时间:2018-07-10 04:39:19

标签: laravel unit-testing phpunit session-cookies

class ExampleTest extends TestCase
{
    public function setUp()
    {
        parent::setUp();
        $this->base_url = config('app.url');

        $response = $this->post($this->base_url . '/auth/login', [
                'username' => 'root',
                'password' => '123',
            ]);

        // how to get the login session cookie?
    }

    public function testStep1()
    {
        // how to set the login session to this POST request?

        $response = $this->post($this->base_url . '/step1', [
                'attr_1' => 'foo',
                'attr_2' => 'bar',
            ]);
        ...
    }

    public function testStep2()
    {
        // how to set the login session to this POST request?

        $response = $this->post($this->base_url . '/step2', [
                'attr_1' => 'abc',
                'attr_2' => 'xyz',
            ]);
        ...
    }
}

从上面的示例代码中,我要实现的是testStep1testStep2必须按(创建对象的向导)的顺序进行。因此,我必须保持相同的会话。

有可能实现吗?

编辑2018-07-10 14:51 UTC + 8

我在调用/auth/login之后尝试了输出,$response->headers->getCookies()的值是

array:1 [
  0 => Symfony\Component\HttpFoundation\Cookie {#940
    #name: "XSRF-TOKEN"
    #value: "eyJpdiI6IjQwUKxYnZlQ0J3N1B0Vkp4VjBEWVE9PSIsInZhbHVlIj782RKOUh2UFhONFwvaVRPUm56YkJ1ekxxSXlCTmtYSFNyRUF3NTdCTWhBMHhEQis1VVU0OUFcL3pKQUcybTFwQjdad1I1em02V1d4bVhDZWR2NFluUTlxdz09IiwibWFjIjoiZWRjYjk2NWI1MTU3YmJlMGEwMDdiNjNkYmVkMjBjMWU3NTRmZjE5NmMyM2EwOTZlNWJmZmYwMmRmYmExMWE1MSJ9"
    #domain: null
    #expire: 1531218886
    #path: "/"
    #secure: false
    #httpOnly: false
    -raw: false
    -sameSite: null
  }
]

并且$response的值是

Illuminate\Foundation\Testing\TestResponse {#843
  +baseResponse: Illuminate\Http\RedirectResponse {#1040
    #request: Illuminate\Http\Request {#856
      #json: null
      #convertedFiles: null
      #userResolver: Closure {#916
        class: "Illuminate\Auth\AuthServiceProvider"
        this: Illuminate\Auth\AuthServiceProvider {#52 …}
        parameters: {
          $guard: {
            default: null
          }
        }
        use: {
          $app: Illuminate\Foundation\Application {#19 …}
        }
        file: "./vendor/laravel/framework/src/Illuminate/Auth/AuthServiceProvider.php"
        line: "85 to 87"
      }
      #routeResolver: Closure {#860
        class: "Illuminate\Routing\Router"
        this: Illuminate\Routing\Router {#167 …}
        use: {
          $route: Illuminate\Routing\Route {#204 …}
        }
        file: "./vendor/laravel/framework/src/Illuminate/Routing/Router.php"
        line: "527 to 529"
      }
      +attributes: Symfony\Component\HttpFoundation\ParameterBag {#870
        #parameters: []
      }
      +request: Symfony\Component\HttpFoundation\ParameterBag {#867
        #parameters: array:2 [
          "username" => "root"
          "password" => "123"
        ]
      }
      +query: Symfony\Component\HttpFoundation\ParameterBag {#911
        #parameters: []
      }
      +server: Symfony\Component\HttpFoundation\ServerBag {#871
        #parameters: array:17 [
          "SERVER_NAME" => "localhost.com"
          "SERVER_PORT" => 80
          "HTTP_HOST" => "localhost.com"
          "HTTP_USER_AGENT" => "Symfony/3.X"
          "HTTP_ACCEPT" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
          "HTTP_ACCEPT_LANGUAGE" => "en-us,en;q=0.5"
          "HTTP_ACCEPT_CHARSET" => "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
          "REMOTE_ADDR" => "127.0.0.1"
          "SCRIPT_NAME" => ""
          "SCRIPT_FILENAME" => ""
          "SERVER_PROTOCOL" => "HTTP/1.1"
          "REQUEST_TIME" => 1531204718
          "PATH_INFO" => ""
          "REQUEST_METHOD" => "POST"
          "CONTENT_TYPE" => "application/x-www-form-urlencoded"
          "REQUEST_URI" => "/auth/login"
          "QUERY_STRING" => ""
        ]
      }
      +files: Symfony\Component\HttpFoundation\FileBag {#878
        #parameters: []
      }
      +cookies: Symfony\Component\HttpFoundation\ParameterBag {#869
        #parameters: []
      }
      +headers: Symfony\Component\HttpFoundation\HeaderBag {#913
        #headers: array:6 [
          "host" => array:1 [
            0 => "localhost.com"
          ]
          "user-agent" => array:1 [
            0 => "Symfony/3.X"
          ]
          "accept" => array:1 [
            0 => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
          ]
          "accept-language" => array:1 [
            0 => "en-us,en;q=0.5"
          ]
          "accept-charset" => array:1 [
            0 => "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
          ]
          "content-type" => array:1 [
            0 => "application/x-www-form-urlencoded"
          ]
        ]
        #cacheControl: []
      }
      #content: null
      #languages: null
      #charsets: null
      #encodings: null
      #acceptableContentTypes: null
      #pathInfo: "/auth/login"
      #requestUri: "/auth/login"
      #baseUrl: ""
      #basePath: null
      #method: "POST"
      #format: null
      #session: Illuminate\Session\EncryptedStore {#924
        #encrypter: Illuminate\Encryption\Encrypter {#919
          #key: b"A╦k>ú8f\x10─ÌÜ8ØýxK\x01²┬Íî·»├\x1A³0▒S┘Ì"
          #cipher: "AES-256-CBC"
        }
        #id: "XPMgecNkwFHbZbujhiuEaBqgMqFTLIqsuIzyvXv"
        #name: "laravel_cookie"
        #attributes: array:11 [
          "_token" => "5lcOcLk9AqvSlWyLdHMKba1lJQ1UqD2rBBVCSav"
          "locale" => "en"
          "_previous" => array:1 [
            "url" => "http://localhost.com/auth/login"
          ]
          "_flash" => array:2 [
            "old" => []
            "new" => []
          ]
          "sess_user_id" => 123
          "sess_user_firstname" => "Foo"
          "sess_user_lastname" => "Bar"
          "sess_role" => "admin"
          "login_web_59ba36add234f940abcf014c987ea4e30989d" => 123
        ]
        #handler: Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler {#925
          -sessionName: null
          -prefetchId: null
          -prefetchData: null
          -newSessionId: null
          -igbinaryEmptyData: "\x00\x00\x00\x02\x14\x00"
        }
        #started: false
      }
      #locale: null
      #defaultLocale: "en"
      -isHostValid: true
      -isForwardedValid: true
      basePath: ""
      format: "html"
    }
    #session: Illuminate\Session\EncryptedStore {#924}
    #targetUrl: "http://localhost.com/dashboard"
    +headers: Symfony\Component\HttpFoundation\ResponseHeaderBag {#1039
      #computedCacheControl: array:2 [
        "no-cache" => true
        "private" => true
      ]
      #cookies: array:1 [
        "" => array:1 [
          "/" => array:1 [
            "XSRF-TOKEN" => Symfony\Component\HttpFoundation\Cookie {#940
              #name: "XSRF-TOKEN"
              #value: "eyJpdiI6IjVyVmRNSmlcL1dYK0VOdiwj8RxamZBPT0iLCJ2YWx1ZSI6IjNSQWFzcVllSEIrSYwZnNNbk1vZ1NERVc2UVdJeGs91D6UG5hNGlHUmRnendJOUVtUnA3Rnk0TnVLYmI5UnJXSTlZR3dxS0wxMElmOFlaWDMzdG9RPT0iLCJtYWMiOiI0ZTZlNTAwNjFkZWFkOTEwN2M1Y2EzMGRjOWMzMmU4NzEzNmM5NWU2MzhhODFjOGJkYTU0YmZlMTM3M2ExNmE3In0="
              #domain: null
              #expire: 1531219118
              #path: "/"
              #secure: false
              #httpOnly: false
              -raw: false
              -sameSite: null
            }
          ]
        ]
      ]
      #headerNames: array:5 [
        "cache-control" => "Cache-Control"
        "date" => "Date"
        "location" => "Location"
        "content-type" => "Content-Type"
        "set-cookie" => "Set-Cookie"
      ]
      #headers: array:4 [
        "cache-control" => array:1 [
          0 => "no-cache, private"
        ]
        "date" => array:1 [
          0 => "Tue, 10 Jul 2018 06:38:38 GMT"
        ]
        "location" => array:1 [
          0 => "http://localhost.com/dashboard"
        ]
        "content-type" => array:1 [
          0 => "text/html; charset=UTF-8"
        ]
      ]
      #cacheControl: []
    }
    #content: """
      <!DOCTYPE html>\n
      <html>\n
          <head>\n
              <meta charset="UTF-8" />\n
              <meta http-equiv="refresh" content="0;url=http://localhost.com/dashboard" />\n
      \n
              <title>Redirecting to http://localhost.com/dashboard</title>\n
          </head>\n
          <body>\n
              Redirecting to <a href="http://localhost.com/dashboard">http://localhost.com/dashboard</a>.\n
          </body>\n
      </html>
      """
    #version: "1.1"
    #statusCode: 302
    #statusText: "Found"
    #charset: null
    +original: null
    +exception: null
  }
}

很明显,会话cookie不在$response->headers->getCookies()中,我不使用actingAs()是因为用户成功登录后,将设置一些会话值,即sess_user_idsess_user_firstnamesess_user_lastnamesess_role ...

3 个答案:

答案 0 :(得分:2)

在第一种情况下,我只会发出发布请求并检查cookie是否存在。

$response = $this->post($this->base_url . '/auth/login', [
                'username' => 'root',
                'password' => '123',
            ]);
$response->assertCookieNotExpired($cookieName);

注意:您可以使用标头获取cookie。 $response->headers->getCookies();,但我认为我们不需要他们。

现在我们知道身份验证的工作原理,我们可以使用Laravel actingAs 帮助方法进行身份验证,然后按以下方式进行请求。

    $user = // get your user.
    $response = $this->actingAs($user)->post($this->base_url . '/step1', [
            'attr_1' => 'foo',
            'attr_2' => 'bar',
        ]);

做出各种喜欢的断言(检查cookie,根据需要进行会话)

$response->assertSessionHas($key, $value);

在转到第三种情况之前,您应该知道最好分别测试每个部分。简而言之,您的测试不应该相互依赖,那么对于第三种情况我们可以做什么?我们知道我们的第三种情况取决于第二种情况,并且我们已经测试了先前的过程。现在我们只想测试我们的第三种情况是否有效。因此,为什么不使用Laravel帮助器 withSession 自己添加此请求所需的会话值。

    $response = $this->actingAs($user)
                     ->withSession(['foo' => 'bar'])
                     ->post($this->base_url . '/step2', [
                           'attr_1' => 'abc',
                           'attr_2' => 'xyz',
                      ]);

现在您可以再次声明。检查所有可用的assertions的列表。

答案 1 :(得分:0)

安装Mockery Mockery是一个简单而灵活的PHP模拟对象框架,可用于与PHPUnit进行单元测试

composer require mockery/mockery --dev

LoginController

public function authenticate(Request $request)
    {

        $credentials = $request->only('email', 'password');

        if (Auth::attempt($credentials)) {

            // Authentication passed...
            // Get the currently authenticated user...
            $user = Auth::user();



            return redirect()->route('profile');
        }else{
            return redirect()->intended('loginform');
        }
    }

示例测试

public function testLoginSuccess()
  {
      $credential = [
          'email' => 'xxx',
          'password' => 'yyy'
      ];

      Auth::shouldReceive('attempt')->once()->withAnyArgs()->andReturn(true);
      Auth::shouldReceive('user')->once()->withAnyArgs()->andReturn(true);

      $response = $this->post('/login',$credential);

      $response->assertRedirect('/profile');
  }

此示例向您展示了如何在单元测试中模拟Auth Facades。

Auth :: attempt返回true
Auth ::用户返回true

您可以根据需要使用Mockery模拟对象。

答案 2 :(得分:0)

检查phpunit.xml,默认情况下SESSION_DRIVER的值设置为'array',将其更改为'file'