To może być trudniejsze do zrobienia, ale uważam, że powoduje to bardziej czytelne testy. Mam nadzieję, że pomożesz mi uprościć to, co zamierzam opisać.
Mój pomysł jest taki, że żąda http. Biorąc pod uwagę facebook, są dwa z nich: 1) /oauth/access_token
(aby uzyskać token dostępu), 2) /me
(aby uzyskać dane o użytkowniku).
Do tego ja chwilowo załączony php
do mitmproxy
stworzyć vcr
urządzenie:
Powiadom php
używać proxy HTTP (dodaj poniższe linie do pliku .env
):
HTTP_PROXY=http://localhost:8080
HTTPS_PROXY=http://localhost:8080
Powiadom php
gdzie certyfikat proxy to: dodaj openssl.cafile = /etc/php/mitmproxy-ca-cert.pem
do php.ini
. Lub o numer curl.cainfo
.
- Ponownie uruchom
php-fpm
.
- Uruchom
mitmproxy
.
-
Uczyń też, że twoja przeglądarka łączy się przez
mitmproxy
.
Zaloguj się do strony, którą tworzysz za pomocą Facebooka (tutaj nie ma TDD).
Prasa z
w mitmproxy
(C
dla mitmproxy
< 0,18), aby usunąć żądanie (przepływu) wykaz przed przekierowaniem do facebook, jeśli zajdzie taka potrzeba. Lub użyj polecenia f
(l
dla mitmproxy
< 0.18), aby odfiltrować dodatkowe żądania.
Pamiętaj, że na twitter będziesz potrzebować league/oauth1-client
w wersji 1.7 lub nowszej. Ta zmieniona z guzzle/guzzle
na guzzlehttp/guzzle
. W przeciwnym razie nie będziesz mógł się zalogować.
Skopiuj dane z mimtproxy
do tests/fixtures/facebook
. Kiedyś formatu yaml
i oto jak to wygląda:
-
request:
method: GET
url: https://graph.facebook.com/oauth/access_token?client_id=...&client_secret=...&code=...&redirect_uri=...
response:
status:
http_version: '1.1'
code: 200
message: OK
body: access_token=...&expires=...
-
request:
method: GET
url: https://graph.facebook.com/v2.5/me?access_token=...&appsecret_proof=...&fields=first_name,last_name,email,gender,verified
response:
status:
http_version: '1.1'
code: 200
message: OK
body: '{"first_name":"...","last_name":"...","email":"...","gender":"...","verified":true,"id":"..."}'
za to można użyć polecenia E
jeśli masz mitmproxy
> = 0,18. Alternatywnie użyj polecenia P
. Kopiuje żądanie/odpowiedź do schowka. Jeśli chcesz, aby mitmproxy
zapisać je bezpośrednio do pliku, możesz uruchomić go z DISPLAY= mitmproxy
.
Nie widzę możliwości korzystania z urządzeń rejestrujących php-vcr
, ponieważ nie testuję całego przepływu pracy.
Dzięki temu mogłem napisać następujące testy (i tak, są one w porządku, gdy wszystkie te wartości są zastępowane kropkami, można kopiować tak, jak jest).
Pamiętaj jednak, że urządzenia są uzależnione od wersji laravel/socialite
. Miałem problem z Facebookiem. W wersji 2.0.16
laravel/socialite
zaczęto wykonywać post requests, aby uzyskać token dostępu. W adresach URL Facebooka jest też api version.
Te oprawy są dla 2.0.14
. Jednym ze sposobów radzenia sobie z tym problemem jest posiadanie również zależności laravel/socialite
w sekcji require-dev
pliku composer.json
(ze ścisłą specyfikacją wersji), aby upewnić się, że socialite
jest odpowiedniej wersji w środowisku programistycznym (mam nadzieję, że composer
zignoruje tę w sekcji require-dev
w środowisku produkcyjnym .) Biorąc pod uwagę, że wykonujesz composer install --no-dev
w środowisku produkcyjnym.
AuthController_HandleFacebookCallbackTest.php
:
<?php
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Auth;
use VCR\VCR;
use App\User;
class AuthController_HandleFacebookCallbackTest extends TestCase
{
use DatabaseTransactions;
static function setUpBeforeClass()
{
VCR::configure()->enableLibraryHooks(['stream_wrapper', 'curl'])
->enableRequestMatchers([
'method',
'url',
]);
}
/**
* @vcr facebook
*/
function testCreatesUserWithCorrespondingName()
{
$this->doCallbackRequest();
$this->assertEquals('John Doe', User::first()->name);
}
/**
* @vcr facebook
*/
function testCreatesUserWithCorrespondingEmail()
{
$this->doCallbackRequest();
$this->assertEquals('[email protected]', User::first()->email);
}
/**
* @vcr facebook
*/
function testCreatesUserWithCorrespondingFbId()
{
$this->doCallbackRequest();
$this->assertEquals(123, User::first()->fb_id);
}
/**
* @vcr facebook
*/
function testCreatesUserWithFbData()
{
$this->doCallbackRequest();
$this->assertNotEquals('', User::first()->fb_data);
}
/**
* @vcr facebook
*/
function testRedirectsToHomePage()
{
$this->doCallbackRequest();
$this->assertRedirectedTo('/');
}
/**
* @vcr facebook
*/
function testAuthenticatesUser()
{
$this->doCallbackRequest();
$this->assertEquals(User::first()->id, Auth::user()->id);
}
/**
* @vcr facebook
*/
function testDoesntCreateUserIfAlreadyExists()
{
$user = factory(User::class)->create([
'fb_id' => 123,
]);
$this->doCallbackRequest();
$this->assertEquals(1, User::count());
}
function doCallbackRequest()
{
return $this->withSession([
'state' => '...',
])->get('/auth/facebook/callback?' . http_build_query([
'state' => '...',
]));
}
}
tests/fixtures/facebook
:
-
request:
method: GET
url: https://graph.facebook.com/oauth/access_token
response:
status:
http_version: '1.1'
code: 200
message: OK
body: access_token=...
-
request:
method: GET
url: https://graph.facebook.com/v2.5/me
response:
status:
http_version: '1.1'
code: 200
message: OK
body: '{"first_name":"John","last_name":"Doe","email":"john.doe\u0040gmail.com","id":"123"}'
AuthController_HandleTwitterCallbackTest.php
:
<?php
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Auth;
use VCR\VCR;
use League\OAuth1\Client\Credentials\TemporaryCredentials;
use App\User;
class AuthController_HandleTwitterCallbackTest extends TestCase
{
use DatabaseTransactions;
static function setUpBeforeClass()
{
VCR::configure()->enableLibraryHooks(['stream_wrapper', 'curl'])
->enableRequestMatchers([
'method',
'url',
]);
}
/**
* @vcr twitter
*/
function testCreatesUserWithCorrespondingName()
{
$this->doCallbackRequest();
$this->assertEquals('joe', User::first()->name);
}
/**
* @vcr twitter
*/
function testCreatesUserWithCorrespondingTwId()
{
$this->doCallbackRequest();
$this->assertEquals(123, User::first()->tw_id);
}
/**
* @vcr twitter
*/
function testCreatesUserWithTwData()
{
$this->doCallbackRequest();
$this->assertNotEquals('', User::first()->tw_data);
}
/**
* @vcr twitter
*/
function testRedirectsToHomePage()
{
$this->doCallbackRequest();
$this->assertRedirectedTo('/');
}
/**
* @vcr twitter
*/
function testAuthenticatesUser()
{
$this->doCallbackRequest();
$this->assertEquals(User::first()->id, Auth::user()->id);
}
/**
* @vcr twitter
*/
function testDoesntCreateUserIfAlreadyExists()
{
$user = factory(User::class)->create([
'tw_id' => 123,
]);
$this->doCallbackRequest();
$this->assertEquals(1, User::count());
}
function doCallbackRequest()
{
$temporaryCredentials = new TemporaryCredentials();
$temporaryCredentials->setIdentifier('...');
$temporaryCredentials->setSecret('...');
return $this->withSession([
'oauth.temp' => $temporaryCredentials,
])->get('/auth/twitter/callback?' . http_build_query([
'oauth_token' => '...',
'oauth_verifier' => '...',
]));
}
}
tests/fixtures/twitter
:
-
request:
method: POST
url: https://api.twitter.com/oauth/access_token
response:
status:
http_version: '1.1'
code: 200
message: OK
body: oauth_token=...&oauth_token_secret=...
-
request:
method: GET
url: https://api.twitter.com/1.1/account/verify_credentials.json
response:
status:
http_version: '1.1'
code: 200
message: OK
body: '{"id_str":"123","name":"joe","screen_name":"joe","location":"","description":"","profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/456\/userpic.png"}'
AuthController_HandleGoogleCallbackTest.php
:
<?php
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Auth;
use VCR\VCR;
use App\User;
class AuthController_HandleGoogleCallbackTest extends TestCase
{
use DatabaseTransactions;
static function setUpBeforeClass()
{
VCR::configure()->enableLibraryHooks(['stream_wrapper', 'curl'])
->enableRequestMatchers([
'method',
'url',
]);
}
/**
* @vcr google
*/
function testCreatesUserWithCorrespondingName()
{
$this->doCallbackRequest();
$this->assertEquals('John Doe', User::first()->name);
}
/**
* @vcr google
*/
function testCreatesUserWithCorrespondingEmail()
{
$this->doCallbackRequest();
$this->assertEquals('[email protected]', User::first()->email);
}
/**
* @vcr google
*/
function testCreatesUserWithCorrespondingGpId()
{
$this->doCallbackRequest();
$this->assertEquals(123, User::first()->gp_id);
}
/**
* @vcr google
*/
function testCreatesUserWithGpData()
{
$this->doCallbackRequest();
$this->assertNotEquals('', User::first()->gp_data);
}
/**
* @vcr google
*/
function testRedirectsToHomePage()
{
$this->doCallbackRequest();
$this->assertRedirectedTo('/');
}
/**
* @vcr google
*/
function testAuthenticatesUser()
{
$this->doCallbackRequest();
$this->assertEquals(User::first()->id, Auth::user()->id);
}
/**
* @vcr google
*/
function testDoesntCreateUserIfAlreadyExists()
{
$user = factory(User::class)->create([
'gp_id' => 123,
]);
$this->doCallbackRequest();
$this->assertEquals(1, User::count());
}
function doCallbackRequest()
{
return $this->withSession([
'state' => '...',
])->get('/auth/google/callback?' . http_build_query([
'state' => '...',
]));
}
}
tests/fixtures/google
:
-
request:
method: POST
url: https://accounts.google.com/o/oauth2/token
response:
status:
http_version: '1.1'
code: 200
message: OK
body: access_token=...
-
request:
method: GET
url: https://www.googleapis.com/plus/v1/people/me
response:
status:
http_version: '1.1'
code: 200
message: OK
body: '{"emails":[{"value":"[email protected]"}],"id":"123","displayName":"John Doe","image":{"url":"https://googleusercontent.com/photo.jpg"}}'
Uwaga. Upewnij się, że masz php-vcr/phpunit-testlistener-vcr
wymagane, i że masz następującą linię w twojej phpunit.xml
:
<listeners>
<listener class="PHPUnit_Util_Log_VCR" file="vendor/php-vcr/phpunit-testlistener-vcr/PHPUnit/Util/Log/VCR.php"/>
</listeners>
Był też problem z $_SERVER['HTTP_HOST']
nie są ustawione, po uruchomieniu testów.Mówię tu o pliku config/services.php
, a mianowicie o przekierowaniu adresu URL. Tak sobie radziłem:
<?php
$app = include dirname(__FILE__) . '/app.php';
return [
...
'facebook' => [
...
'redirect' => (isset($_SERVER['HTTP_HOST']) ? 'http://' . $_SERVER['HTTP_HOST'] : $app['url']) . '/auth/facebook/callback',
],
];
Niezbyt piękna, ale nie udało mi się znaleźć lepszego sposobu. Zamierzałem użyć tam config('app.url')
, ale nie działa to w plikach konfiguracyjnych.
UPD Można pozbyć setUpBeforeClass
części poprzez usunięcie tej metody uruchamiania testów i uaktualniania żądania część opraw z zapisów co magnetowidu. W rzeczywistości wszystko można zrobić samemu (vcr
) (nr).
Myślę, że możesz chcieć 'Socialite :: shouldReceive ('driver-> redirect')'. – ceejayoz
@ceejayoz To nie działa, narzeka, że nie widzi metody "driver-> redirect' –