Přihlášení uživatele při testování

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
p4nther
Člen | 10
+
0
-

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
+
+1
-

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
+
0
-

Š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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

Pomůže spouštět testy takto:
phpunit --stderr

Editoval Fafin (13. 9. 2012 16:57)