I'm creating functionnal tests on a Symfony 3.4 application.
<?php
namespace AppBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\HttpFoundation\Response;
class UserControllerTest extends WebTestCase
{
/**
* Connect to the website while being logged in
* Logs in with (admin, password : a)
*/
public function connection()
{
$client = static::createClient();
$container = static::$kernel->getContainer();
$session = $container->get('session');
// Get the user (has to exist in the database)
$person = self::$kernel->getContainer()->get('doctrine')->getRepository('AppBundle:User')->findOneByUsername('admin');
$token = new UsernamePasswordToken($person, null, 'main', $person->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
// Return the client
return $client;
}
public function accessEditPage()
{
$client = $this->connection();
$crawler = $client->request('GET', '/user/');
$this->assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode());
$this->assertContains(
'Liste des utilisateurices',
$client->getResponse()->getContent()
);
// Select the button of the user created for the test
// Wont work if there are already more than 10 users in the database
$link = $crawler
->filter('tr > td > a:contains("")')
->last()
->link()
;
$crawler = $client->click($link);
return array($client,$crawler);
}
/**
* Create a new user
*/
public function testCreate()
{
$client = $this->connection();
$crawler = $client->request('GET', '/user/new');
$this->assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode());
// Vérifie si la page affiche le bon texte
$this->assertContains(
'Enregistrer',
$client->getResponse()->getContent()
);
// Select the form and fill its values
$form = $crawler->selectButton(' Créer')->form();
$values = $form->getPhpValues();
$values['appbundle_user']['username'] = 'Jean';
$values['appbundle_user']['plainPassword']['first'] = 'motdepasse';
$values['appbundle_user']['plainPassword']['second'] = 'motdepasse';
$crawler = $client->request($form->getMethod(), $form->getUri(), $values,$form->getPhpFiles());
$crawler = $client->followRedirect();
$this->assertContains(
'Jean',
$client->getResponse()->getContent()
);
}
}
Currently, my Controller tests create databases entries and depends on existing ones and that's a problem.
I want to mock the repositories used in the controller to avoid creating entries when I test my controllers but I haven't found helpful documentation about it. As I can't find documentation, I also wonder if what I want to do is a good practice or not.
Related
I have a class I want to test that uses this module PHP HTTP client for Emarsys webservice, but when I try to test it, I will always get $response as "Credentials are invalid" from the module itself.
Here's a snippet of my code: (Given that I was able to correctly create my setUp() for Test Class since I was able to use it for other tests)
Test.php
Class TestClass extends UnitTestCase {
public function testCreateWithValidEmail() {
$newsletter = new Newsletter();
$form = new FormState();
$form->setValue('email', 'abc#def.ghi');
$response = $newsletter->register($form);
// Assertion here
}
}
Class.php
use Snowcap\Emarsys\CurlClient;
use Snowcap\Emarsys\Client;
Class Newsletter {
public function register(FormStateInterface $state){
$emailData = $state->getValue('email');
$httpClient = new CurlClient();
$client = new Client($httpClient, $api_username, $api_secret);
$someData = [
"3" => $emailData, // since 3 is the index ID for email
// ...more data here
];
$response = $client->createContact($someData);
}
}
Do I have to create a mock of something here to pass a dummy api and secret then force a valid response from createContact?
You are in the good direction. But that Newsletter class needs the $httpClient injected.
So you will be able to do:
$client = $this->getMockBuilder(Snowcap\Emarsys\CurlClient::class)
->disableOriginalConstructor()
->getMock();
$response = $this->getMockBuilder(ResponseInterface::class)
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->willReturn(Response::HTTP_OK);
$client->expects($this->any())
->method('createContact')
->with($someData)
->will($this->returnValue($response));
$newsletter = new Newsletter($client);
$response = $newsletter->register($form);
// Assertion here
I am currently working on unit testing my symfony 2.8 based admin area.
So I wrote a small basic test for the dashboard: The tests checks that
a) If the user is currently not logged in, there should be a redirect to the login page.
b) If the user is logged in, the dashboard should be shown.
In order to "log in" the user ( = to create an active session) I came up with a small helper function that is based on the respective cookbook article from the symfony documentation: How to Simulate Authentication with a Token in a Functional Test
This however does not seem to work. My initial tests fails. So I have added another request to a dummy controller that only prints out session information. This shows that while the seems to be set correctly, the current user information and the security token storage seem to be incorrect - as I get the default "AnonymousToken" and a null return from the getUser method.
Here is my test code:
<?php
namespace GOC\Bundle\AdminBundle\Tests\Controller;
use FOS\RestBundle\Util\Codes;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use GOC\Bundle\FrameworkBundle\Model\ContextInterface;
use GOC\Bundle\FrameworkBundle\Tests\WebTestCase;
class DashboardControllerTest extends WebTestCase
{
const BASE_URL = '';
/**
* #var ContextInterface|null
*/
protected $context;
public static function setUpBeforeClass()
{
$client = static::createClient([
'environment' => 'test',
'debug' => false,
], [
'HTTP_HOST' => 'demo-cms.dev',
]);
$container = $client->getContainer();
$kernel = $container->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput(array(
'command' => 'doctrine:mongodb:fixtures:load',
));
$output = new NullOutput();
$application->run($input, $output);
}
/**
* Testing whether the backend is not publicly accessible
*/
public function testFirewallRedirect()
{
$client = static::createClient();
$client->request('GET', $this->buildUrl('/admin/dashboard'));
$response = $client->getResponse();
$this->assertTrue($client->getResponse()->isRedirect($this->buildUrl('http://localhost/admin/login')));
$crawler = $client->request('GET', $response->headers->get('location'));
$this->assertEquals(
1,
$crawler->filter('html:contains("Willkommen")')->count()
);
}
/**
* Testing whether the backend is not publicly accessible
*/
public function testFirewallAccess()
{
$client = static::createClient([
'environment' => 'test',
'debug' => false,
], [
'HTTP_HOST' => 'demo-cms.dev',
]);
$this->logIn($client);
$client->request('GET', $this->buildUrl('/admin/dashboard'));
$response = $client->getResponse();
// This fails...
//$this->assertEquals(Codes::HTTP_OK, $response->getStatusCode());
// Debug statements
$client->request('GET', $this->buildUrl('/forgot-password-sent'));
$response = $client->getResponse();
dump($response);
}
/**
* #param Client $client
*/
protected function logIn(Client $client)
{
$container = $client->getContainer();
$repository = $container->get('goc_account.user_manager')->getRepository();
$user = $repository->getUserByUsername('admin#gardenofconcepts.com', $this->getContext($client));
$firewall = 'main';
$token = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
$container->get('security.token_storage')->setToken($token);
$session = $container->get('session');
// Saw this somewhere else, makes no difference though
//$session = new Session(new MockArraySessionStorage());
//$session->start();
//$container->set('session', $session);
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);
}
/**
* #param $url
*
* #return string
*/
protected function buildUrl($url)
{
return $this::BASE_URL . $url;
}
/**
* #param Client $client
*
* #return ContextInterface
*/
protected function getContext(Client $client)
{
if ($this->context) {
return $this->context;
}
$this->context = $client->getContainer()->get('goc_framework.context_manager')->getContextByName('demo');
return $this->context;
}
}
Thanks in advance for any help - it's highly appreciated !
We are not trying to mock the token but actually create one with the following function (which might be adaptable to your situation):
protected function createAuthenticatedClient($username, $password)
{
$client = static::createClient();
$client->request(
'POST',
'/api/v1/login_check',
array(
'username' => $username,
'password' => $password,
)
);
$data = json_decode($client->getResponse()->getContent(), true);
$client = static::createClient();
$client->setServerParameter('HTTP_Authorization', sprintf('Bearer %s', $data['token']));
return $client;
}
After using this like
$client = $this->createAuthenticatedClient('user', 'password');
every request sent will be attached with our actual Bearer.
I have entity service and I create controller and action get, create, edit and deleted. I look for test and I don't know why I have error? I can enter for this rout and have data, and work fine, but if create client and get status code have 302
But when I comment in security.yml
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
#- { path: ^/admin, roles: ROLE_ADMIN }
test passed almost all only in the late fall
How to create client with ROLE_ADMIN ??
and this test
public function testCompleteScenario()
{
// Create a new client to browse the application
$client = static::createClient();
// Create a new entry in the database
$crawler = $client->request('GET', '/admin/services/');
$this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /admin/services/");
$crawler = $client->click($crawler->selectLink('Create a new entry')->link());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'artel_profilebundle_services[services]' => 'Test',
// ... other fields to fill
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check data in the show view
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")');
// Edit the entity
$crawler = $client->click($crawler->selectLink('Edit')->link());
$form = $crawler->selectButton('Update')->form(array(
'artel_profilebundle_services[services]' => 'Foo',
// ... other fields to fill
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check the element contains an attribute with value equals "Foo"
$this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]');
// Delete the entity
$client->submit($crawler->selectButton('Delete')->form());
$crawler = $client->followRedirect();
// Check the entity has been delete on the list
// **this is 51 line**
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent());
}
and I have
' does not match PCRE pattern "/Foo/".
/home/ivan/host/test/src/Artel/AdminBundle/Tests/Controller/ServicesControllerTest.php:51
where error ?
UPDATE
change
class ServicesControllerTest extends WebTestCase
{
private $client = null;
public function setUp()
{
$this->client = static::createClient();
}
public function logIn()
{
$session = $this->client->getContainer()->get('session');
$firewall = 'default';
$token = new UsernamePasswordToken('admin', null, $firewall, array('ROLE_ADMIN'));
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
public function testCompleteScenario()
{
// Create a new client to browse the application
$this->logIn();
// Create a new entry in the database
$crawler = $this->client->request('GET', '/admin/services/');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /admin/services/");
$crawler = $this->client->click($crawler->selectLink('Create a new entry')->link());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'artel_profilebundle_services[services]' => 'Test',
// ... other fields to fill
));
$this->client->submit($form);
$crawler = $this->client->followRedirect();
// Check data in the show view
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")');
// Edit the entity
$crawler = $this->client->click($crawler->selectLink('Edit')->link());
$form = $crawler->selectButton('Update')->form(array(
'artel_profilebundle_services[services]' => 'Foo',
// ... other fields to fill
));
$this->client->submit($form);
$crawler = $this->client->followRedirect();
// Check the element contains an attribute with value equals "Foo"
$this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]');
// Delete the entity
$this->client->submit($crawler->selectButton('Delete')->form());
$crawler = $this->client->followRedirect();
// this is line 73
$this->assertNotRegExp('/Foo/', $this->client->getResponse()->getContent());
}
}
in this step I have error
$this->assertNotRegExp('/Foo/', $this->client->getResponse()->getContent());
after deleted test service function assertNotRegExp try to find in content but error something with regular I dint know. After test I have all html my page /admin/services/ and error
' does not match PCRE pattern "/Foo/".
/home/ivan/host/test/src/Artel/AdminBundle/Tests/Controller/ServicesControllerTest.php:73
where error ?
You have to make your request authenticated.
Add the following code to your test class :
private $client = null;
public function setUp()
{
$this->client = static::createClient();
}
private function logIn()
{
$session = $this->client->getContainer()->get('session');
$firewall = 'secured_area';
$token = new UsernamePasswordToken('admin', null, $firewall, array('ROLE_ADMIN'));
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
And use it in your test method before create the client:
public function testCompleteScenario()
{
$this->logIn();
// Do your logic
}
See Simulate authentication in test
I am attempting to log a user in programmatically in my functional test on SF 2.7 and FOSUserBundle dev-master. I have already found a good reference to log a user in via SO in this answer - Symfony2 - Tests with FOSUserBundle
The problem is that the second answer, logging the user in programmatically, doesn't work. Here is my code:
<?php
namespace Test\BackEnd\UserBundle\Controller;
use Test\Shared\CoreBundle\Tests\AbstractControllerTest;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\Tools\SchemaTool;
use FA\BackEnd\UserBundle\DataFixtures\ORM\LoadUserData;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class DefaultController extends AbstractControllerTest
{
public function setUp()
{
$this->client = static::createClient();
$container = $this->client->getContainer();
$doctrine = $container->get('doctrine');
$em = $doctrine->getManager();
$schemaTool = new SchemaTool($em);
$metadata = $em->getMetaDataFactory()->getAllMetadata();
// Drop and recreate tables for all entities
$schemaTool->dropSchema($metadata);
$schemaTool->createSchema($metadata);
$loader = new Loader();
$user = new LoadUserData();
$user->setContainer($container);
$loader->addFixture($user);
$purger = new ORMPurger();
$executor = new ORMExecutor($em, $purger);
$executor->execute($loader->getFixtures());
$session = $container->get('session');
$userManager = $container->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('username' => 'test'));
$firewall = 'default';
$token = new UsernamePasswordToken($user, $user->getPassword(), $firewall, $user->getRoles());
self::$kernel->getContainer()->get('security.token_storage')->setToken($token);
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
public function testProfile()
{
//$this->createAuthorizedClient();
$token = $this->client->getContainer()->get('security.token_storage')->getToken();
$this->client->request('GET', '/profile/');
$this->assertEquals(
200,
$this->client->getResponse()->getStatusCode(),
"/profile isn't accessible"
);
}
}
Whenever I set a break point before the route gets executed, the token is return correctly:
Whenever I get to the function getUser() used by the Controller (http://api.symfony.com/2.7/Symfony/Bundle/FrameworkBundle/Controller/Controller.html#method_getUser) PHPStorm returns an empty token as viewed here:
So I decided to try the following code to log a user in, and it works.
$crawler = $this->client->request('GET', '/login');
$form = $crawler->selectButton('_submit')->form(array(
'_username' => 'test',
'_password' => 'test123',
));
$this->client->submit($form);
$this->client->followRedirect();
Am I not doing something properly whenever I log the user in programmatically? Is the session not being set properly?
Thanks!
Rat
I use this:
protected function createAuthorizedClient()
{
$client = static::createClient();
$container = $client->getContainer();
$session = $container->get('session');
$userManager = $container->get('fos_user.user_manager');
$loginManager = $container->get('fos_user.security.login_manager');
$firewallName = $container->getParameter('fos_user.firewall_name');
$user = $userManager->findUserBy(array('username' => 'USERNAME'));
$loginManager->loginUser($firewallName, $user);
// save the login token into the session and put it in a cookie
$container->get('session')->set('_security_' . $firewallName,
serialize($container->get('security.context')->getToken()));
$container->get('session')->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
$this->client = $client;
}
and then in your test:
public function testMiInfo()
{
$this->createAuthorizedClient();
//else..
}
I try make test with authentication fosuserbundle, but still is fail, i find solution for symfony 2.3 but it doesn't works
https://gist.github.com/deltaepsilon/6391565
i also try create client by two funcitons
protected function createAuthorizedClient2()
{
$client = static::createClient();
$container = $client->getContainer();
$session = $container->get('session');
/** #var $userManager \FOS\UserBundle\Doctrine\UserManager */
$userManager = $container->get('fos_user.user_manager');
/** #var $loginManager \FOS\UserBundle\Security\LoginManager */
$loginManager = $container->get('fos_user.security.login_manager');
$firewallName = $container->getParameter('fos_user.firewall_name');
$user = $userManager->findUserBy(array('username' => 'admin'));
$loginManager->loginUser($firewallName, $user);
// save the login token into the session and put it in a cookie
$container->get('session')->set('_security_' . $firewallName, serialize($container->get('security.context')->getToken()));
$container->get('session')->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
return $client;
}
protected function createAuthorizedClient()
{
$client = static::createClient();
$container = static::$kernel->getContainer();
$session = $container->get('session');
$person = self::$kernel->getContainer()->get('doctrine')->getRepository('BergUserDataBundle:UserLogin')->findOneByUsername('admin');
$token = new UsernamePasswordToken($person, null, 'main', $person->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
return $client;
}
This is no longer the recommended way of testing with an authenticated client.
The new recommended way is much simpler - to submit plain old HTTP credentials and then tell your test environment firewall to authenticate via this method instead of the FOS User Provider.
See http://symfony.com/doc/current/cookbook/testing/http_authentication.html