Přihlášení uživatele při testování
- p4nther
- Člen | 10
Jak vyřešit přístup na stránky (akce presenteru), ke kterým je vyžadovaný přihlášení? Když zkouším použít přímo metodu „login“, tak skončím na hlášce „Cannot regenerate session ID“, protože PHPUnit už stihl něco vypsat do výstupu.
Zatím mám kód, kterej využívá metodu authenticate, ale kontrola přes metodu „isLoggedIn“ neprojde a test skončí.
$username = "test".rand(1, 100);
$password = "test";
$this->database->table("user")->insert(array(
"username" => $username,
"password" => $password
));
$this->object->user->setAuthenticator(new UserAuth($this->database->table('user')));
$auth = $this->object->user->getAuthenticator()->authenticate(array($username, $password));
$this->assertTrue($this->object->user->isLoggedIn(), "Prihlaseni se nezdarilo");
Vím, že tyhle testy by šly vyřešit i přes Selenium, ale to je hrozně pomalý..
Řešení jsem hledal tady na fóru i přes Google – bez úspěchu..
Web běží na PHP 5.3 a Nette ve verzi 2.0, pro připojení k databázi používám Nette\Database.
- duke
- Člen | 650
Můžeš si naimplementovat vlastní třídu implementující rozhraní Nette\Security\IUserStorage a tou v configu nahradit Nette\Security\UserStorage (což je třída, která používá session).
Co se přihlašování v rámci testů týče, Authenticator vůbec
nepotřebuješ (pokud tedy netestuješ přímo tu autentikaci). Stačí metodě
User::login
zadat jako parametr přímo identitu (tj. objekt
implementující rozhraní Nette\Security\IIdentity), tj. to, co normálně
Authenticator vrací při úspěšném ověření
přihlašovacích údajů…
- llook
- Člen | 407
Šel bych na to mockováním IUserStorage. Cca nějak takto:
$context = new \SystemContainer;
\Nette\Environment::setContext($context);
$context->userStorageMock = $this->getMock('Nette\Security\IUserStorage');
$context->classes['nette\security\iuserstorage'] = 'userStorageMock';
$context->userStorageMock->expects($this->any)
->method('isAuthenticated')
->will($this->returnValue(true));
$context->userStorageMock->expects($this->any)
->method('getIdentity')
->will($this->returnValue(new Identity(1)));
- p4nther
- Člen | 10
Tak sem zkusil obě varianty.
Pokud se pokusím o přihlášení přes metodu „login“, dostanu „Nette\InvalidStateException: Cannot regenerate session ID after HTTP headers have been sent (output started at D:\wamp\bin\php\php5.3.0\PEAR\PHPUnit\Util\Printer.php:173).“
Po menší úpravě sem zkusil to mockování, ale něco asi dělám špatně. Test stále neprochází.. Kód vypadá teď takhle:
public function setUp() {
$context = new \SystemContainer;
\Nette\Environment::setContext($context);
//$context = Nette\Environment::getContext(); //s touhle variantou to dopadne stejne
if (!$context->hasService("userStorageMock")) {
$context->userStorageMock = $this->getMock('Nette\Security\IUserStorage');
$context->classes['nette\security\iuserstorage'] = 'userStorageMock';
$context->userStorageMock->expects($this->any())
->method('isAuthenticated')
->will($this->returnValue(true));
$context->userStorageMock->expects($this->any())
->method('getIdentity')
->will($this->returnValue(new Nette\Security\Identity(1)));
}
$this->object = new ArticlePresenter(Nette\Environment::getContext());
}
Když to pak v samotným testu ověřím přes tenhle kód, tak to neprojde..
$this->assertTrue($this->object->user->isLoggedIn(), "Prihlaseni se nezdarilo");
- duke
- Člen | 650
p4nther napsal(a):
Pokud se pokusím o přihlášení přes metodu „login“, dostanu „Nette\InvalidStateException: Cannot regenerate session ID after HTTP headers have been sent (output started at D:\wamp\bin\php\php5.3.0\PEAR\PHPUnit\Util\Printer.php:173).“
Pokud ti to hlásí toto, znamená to, že to stále používá Nettí
třídu UserStorage
, kterou právě potřebuješ nahradit (ať už
tak či onak, tj. ať už přes speciální config, či dodatečně modifikací
$context).
A nejsem si jist zda llookovo:
$context->classes['nette\security\iuserstorage'] = 'userStorageMock';
… dostatečným způsobem tu službu v systémovém kontejneru nahrazuje. Spíš bych si tipnul, že ne.
Když to pak v samotným testu ověřím přes tenhle kód, tak to neprojde..
$this->assertTrue($this->object->user->isLoggedIn(), "Prihlaseni se nezdarilo");
Předpokládám, že $this->object->user
je
Nette\Security\User
. Metoda isLoggedIn
jen volá
storage->isAuthenticated
. Protožes u mockovaného objektu
nastavil, že isAuthenticated
má vždy vracet TRUE
,
tak to, že to selhává, znamená, že služba User
používá
nejspíš stále ten původní, který se snažíš mockovat (to mě utvrzuje
v mnou výše zmíněné obavě, že zvolená úprava context je
nedostačující).
Editoval duke (9. 4. 2012 13:25)
- p4nther
- Člen | 10
duke napsal(a):
A nejsem si jist zda llookovo:
$context->classes['nette\security\iuserstorage'] = 'userStorageMock';
… dostatečným způsobem tu službu v systémovém kontejneru nahrazuje. Spíš bych si tipnul, že ne.
Máš pravdu, nenahrazuje. Nakonec jsem musel vytvořit svoji implementaci IUserStorage a testovací config.neon. Zapsat to do něj byl trochu oříšek, ale podařilo se. Pokud by někdo řešil něco podobnýho tak je to takhle:
services:
nette.userStorage:
class: TestUserStorage
Nic víc není potřeba. Díky za rady ;-)
- p4nther
- Člen | 10
Shrnul jsem můj postup do článku na blogu, pokud by někdo další řešil podobný problém a nevěděl si rady – Testování privilegovaných akcí v Nette
- Svaťa Šimara
- Člen | 98
Pomůže spouštět testy takto:
phpunit --stderr
Editoval Fafin (13. 9. 2012 16:57)