I tried to test my component function through unit testing.
My component function below
public function userRole() {
$loginId = $this->Cookie->read('Admin.login_id');
$name = $this->Cookie->read('Admin.name');
$role = $this->Cookie->read('Admin.role');
if (empty($loginId) || empty($name)){
return false;
}
$adminsORM = TableRegistry::get('Admins');
$admin = $adminsORM->find('all', [
'conditions' => ['login_id' => $loginId, 'name' => $name, 'disable' => 0]
])->first();
return empty($admin)? false : $admin->role;
}
And my component testing function below
public $Acl;
public function setUp()
{
parent::setUp();
$registry = new ComponentRegistry();
$this ->Acl = new AclComponent($registry);
}
public function testUserRole()
{
// Test our adjust method with different parameter settings
$this->Cookie->write('Admin.login_id', 'demo12');
$this->Cookie->write('Admin.role', 1);
$this->Cookie->write('Admin.name', 'demo 12');
$output = $this->Acl->userRole();
$this->assertResponseOk();
}
composer testing code
vendor/bin/phpunit --filter testUserRole /d/xampp/htdocs/admin/admin/tests/TestCase/Controller/Component/AclComponentTest.php
error
Notice Error: Undefined property: App\Test\TestCase\Controller\Component\AclComponentTest::$Cookie in [D:\xampp\htdocs\admin\admin\tests\TestCase\Controller\Component\AclComponentTest.php, line 31]
As the error suggests, there is no $this->Cookie property in your unit test. I can only assume that $this->Cookie in your component refers to the Cookie component (which btw is deprecated as of CakePHP 3.5).
If you need to prepare cookies for a regular unit test, and not a controller/integration test (where you could to use the IntegrationTestCase::cookie(), IntegrationTestCase::cookieEncrypted(), IntegrationTestCase::assertResponseOk() methods), then you have to write the cookies directly to the request object, and make sure that you make it available to the component.
Check out the example in the Cookbook on how to test components, it should look something like this:
namespace App\Test\TestCase\Controller\Component;
use App\Controller\Component\MyComponent;
use Cake\Controller\Controller;
use Cake\Controller\ComponentRegistry;
use Cake\Http\ServerRequest;
use Cake\Http\Response;
use Cake\TestSuite\TestCase;
class MyComponentTest extends TestCase
{
public $component = null;
public $controller = null;
public function setUp()
{
parent::setUp();
$request = new ServerRequest();
$response = new Response();
$this->controller = $this->getMockBuilder('Cake\Controller\Controller')
->setConstructorArgs([$request, $response])
->setMethods(null)
->getMock();
$registry = new ComponentRegistry($this->controller);
$this->component = new MyComponent($registry);
}
// ...
}
You can then either define the cookies in the setUp() method, so that they are available in all tests, or you can define them individually per test. Also note that if you're working with encrypted cookies, you should use CookieCryptTrait::_encrypt() to encrypt the cookie data.
// ...
use Cake\Utility\CookieCryptTrait;
use Cake\Utility\Security;
protected function _getCookieEncryptionKey()
{
// the cookie component uses the salt by default
return Security::getSalt();
}
public function testUserRole()
{
$data = [
'login_id' => 'demo12',
'role' => 1,
'name' => 'demo 12'
];
// the cookie component uses `aes` by default
$cookie = $this->_encrypt($data, 'aes');
$request = new ServerRequest([
'cookies' => [
'Admin' => $cookie
]
]);
$this->controller->request = $request;
$output = $this->Acl->userRole();
$this->assertEquals('expected value', $output);
}
See also
Cookbook > Testing > Testing Components
API > \Cake\Utility\CookieCryptTrait
Based on the testing documentation, in order to set your cookies during your test cases, you need to use the function $this->cookieEncrypted('my_cookie', 'Some secret values'):
$this->cookieEncrypted('Admin.login_id', 'demo12');
$this->cookieEncrypted('Admin.role', 1);
$this->cookieEncrypted('Admin.name', 'demo 12');
Related
I am trying to test a Service which preprocessed a form and finally saves it. Within the creating of that form:
$this->container->get('security.token_storage')->getToken()->getUser();
is called to get currently logged in user as a default value for a field.
Right now I am having this (extending Symfony\Bundle\FrameworkBundle\Test\WebTestCase):
private $pages;
private $formFactory;
protected function setUp()
{
self::bootKernel();
$client = self::$kernel->getContainer()->get('test.client');
$client->setServerParameters([
'HTTP_HOST' => 'ajax.localhost.dev:10190',
'CONTENT_TYPE' => 'application/json',
'HTTP_X-Requested-With' => 'XMLHttpRequest',
'HTTP_USER_AGENT' => 'Symfony/2.0',
'PHP_AUTH_USER' => 'root',
'PHP_AUTH_PW' => 'root#localhost.dev'
]);
$this->pages = self::$kernel->getContainer()->get('app.pages');
$this->formFactory = self::$kernel->getContainer()->get('form.factory');
}
public function testNewPage() {
$page = new Page();
//shortened
$form = $this->formFactory->create(PageType::class, $page);
}
But that gives me the error:
Call to a member function getUser() on null
What shows that there is no security token.
How can I come over that?
UPDATE
Thanks to the comments of #LBA I tried that code, with no luck:
$session = self::$kernel->getContainer()->get('session');
$token = new UsernamePasswordToken('root', 'root', 'main', ['ROLE_USER', 'ROLE_ROOT']);
$session->set('_security_main', serialize($token));
$session->save();
The part with setting a Cookie as described here is missing, since the $kernel has no method getCookieJar()
I could finally make it work like so:
protected function setUp()
{
self::bootKernel();
$root = new User();
$root->setUsername('root');
$root->setPassword('root');
$root->setEmail('root#localhost.dev');
$token = new UsernamePasswordToken($root, null, 'main', ['ROLE_USER', 'ROLE_ROOT']);
self::$kernel->getContainer()
->get('security.token_storage')
->setToken($token);
$this->pages = self::$kernel->getContainer()->get('app.pages');
$this->formFactory = self::$kernel->getContainer()->get('form.factory');
}
BUT BUT BUT Even if it is possible to solve that problem, the real issue in this case is, to have that $this->container->get('security.token_storage')->getToken()->getUser(); call with the Form, since this breaks the form in a test case. The pattern to prevent such a thing from happening is dependency injection, what I have missed to apply on the form type.
So the better solution would be (in the Form extending AbstractType):
public function configureOptions(OptionsResolver $resolver)
{
$this->setDefined(['user]);
}
And finally create the form like so (in Controller or TestCase)
Within a UnitTest:
$user = new User();
and in the controller:
$user = $this->container->get('security.token_storage')->getToken()->getUser();
$form = $this->formFactory->create(TheFormType::class,
<some data object>,
['user' => $user]);
I am a beginner in unit testing in CakePHP. My version of CakePHP is 2.5.2 and I am using cake test suite 2.5.2. I want to do controller testing. I have tried all methods given in cookbook. I think this is too much complex code for given example in cookbook. I have customised routes for my application. I can invoke method showed below by calling : http://localhost/api/v1/networks/
This is the simplest method in my controller. How can i start testing on this method and what mocks should i need?
public function index() {
if (!$this->request->is('Get')) {
throw new MethodNotAllowedException(__('HTTP request Method Not allowed..'));
}
$networks = $this->Network->UserNetwork->getNetworks($this->current_user);
$returnObject = array('message' => 'Networks found successfully',
'data' => $networks
);
return $this->_sendResponse($returnObject, 200);
}
what i have tried so far is:
<?php
App::uses('NetworksController', 'Controller');
class NetworksControllerTestCase extends ControllerTestCase {
public $fixtures = array(
'app.network',
);
public function setUp() {
parent::setUp(); // TODO: Change the autogenerated stub
$this->Network = $this->generate('Networks',
array('models' => array('network','usernetwork' => array('getNetworks'))));
}
public function testIndex() {
$result = $this->testAction('/api/v1/networks/index', array('return' => 'vars'));
debug($result);
}}
I am using the mockery to test a method that make a lot of doctrine repository invocations with different repositories.
This is the method that i set up all my repository mocks:
public function testService()
{
$mockDoctrine = $this->getMockDoctrine();
$mockDoctrine->shouldReceive('getRepository')->once()
->andReturn($this->getRepositoryAMock());
$mockDoctrine->shouldReceive('getRepository')->once()
->andReturn($this->getRepositoryBMock());
$mockDoctrine->shouldReceive('getRepository')->once()
->andReturn($this->getRepositoryCMock());
//here is where i hit my test
$products = $this->service->fire(1, 1);
$this->assertInstanceOf('Illuminate\Support\Collection', $products);
foreach ($products as $v) {
$this->assertInstanceOf('Illuminate\Support\Collection', $v);
}
}
This is the method that i mock the Doctrine:
public function getMockDoctrine()
{
$mockDoctrine = \App::make('Doctrine');
$mockDoctrine->shouldReceive('persist')
->andReturn(true);
$mockDoctrine->shouldReceive('flush')
->andReturn(true);
return $mockDoctrine;
}
These are my repositories mock
public function getRepositoryAMock()
{
$repository = \Mockery::mock('MyARepository');
$repository->shouldReceive('findBy')
->with(['paramA' => 1, 'paramB' => 1])
->andReturn($this->getMockA());
return $repository;
}
public function getRepositoryBMock()
{
$repository = \Mockery::mock('MyBRepository');
$repository->shouldReceive('findById')
->with(1)
->andReturn($this->getMockA());
return $repository;
}
public function getRepositoryCMock()
{
$repository = \Mockery::mock('MyCRepository');
$repository->shouldReceive('findOneBy')
->with(['paramA' => 1, 'paramB' => 1])
->andReturn($this->getMockA());
return $repository;
}
This is where in fact i set the return of my mock
public function getMockA()
{
$obj = new MyClass();
$reflection = new \ReflectionClass($obj);
$id = $reflection->getProperty('id');
$id->setAccessible(true);
$id->setValue($obj, 1);
$obj
->setLogin('foo')
->setPassword('bar')
->setCode(1);
return $obj;
}
And then i receive an error like this:
1) MyClassTest::testService
BadMethodCallException: Method Mockery_2_ClassBRepository::findOneBy() does not exist on this mock object
Assuming that i have 3 methods with repositories being called in testService() method, the method that mockery is not finding is in the third one, but mockery thinks it is in the second, so obviously he won't find, because in the second one, does not exist the "findOneBy()" doctrine method just in the third.
How can i solve this ?
You should be able to use mockery's with().
For example:
$mockDoctrine
->shouldReceive('getRepository')
->with('MyAReposiotry')->once()
->andReturn($this->getRepositoryAMock());
And like that for every repository (with different value in with).
But I would rather inject repositories in that service instead of getting it from entity manager inside service. It is way better for testing. Take a look at this blog post.
Thanks to Mr Ivan,
Now my method is working.
In my real class, i was getting the repository with the entity class,
like:
Doctrine::getRepository('MyEntityClassA')
->findBy(['paramA' => 1, 'paramB' => 1]);
so i changed my test method to use the "with" passing the entity class:
public function testService()
{
$mockDoctrine = $this->getMockDoctrine();
$mockDoctrine->shouldReceive('getRepository')
->with('MyEntityClassA')->once()
->andReturn($this->getRepositoryAMock());
$mockDoctrine->shouldReceive('getRepository')->once()
->with('MyEntityClassB')->once()
->andReturn($this->getRepositoryBMock());
$mockDoctrine->shouldReceive('getRepository')->once()
->with('MyEntityClassC')->once()
->andReturn($this->getRepositoryCMock());
//here is where i hit my test
$products = $this->service->fire(1, 1);
$this->assertInstanceOf('Illuminate\Support\Collection', $products);
foreach ($products as $v) {
$this->assertInstanceOf('Illuminate\Support\Collection', $v);
}
}
I have installed php unit in my local server, but I dont understand (reading the php unit help) how to test my action create. My action is this, and the only thing I want to test is if it saves on database.
/**
* Creates a new model.
* If creation is successful, the browser will be redirected to the 'view' page.
*/
public function actionCreate() {
$_class = $this->getClassName();
$model = new $_class;
if (isset($_POST)) {
$model->attributes = $_POST;
$this->armaMensajeABMGrilla($model->save(), $model);
}
$this->renderPartial('create', array(
'model' => $model,), false, true);
}
protected function armaMensajeABMGrilla($guardoOk, $modelo = null) {
if ($guardoOk == true) {
$this->respuestaJSON = array('success' => true, 'mensaje' => 'ok');
} else {
$erroresMensaje = 'Listado de Errores: <br/><ul>';
$i = 0;
if (isset($modelo->errors)) {
foreach ($modelo->errors as $error) {
$erroresMensaje .= '<li>Error(' . $i . '): ' . $error[0] . '</li>';
$i++;
}
$erroresMensaje.='</ul>';
}
$this->respuestaJSON = array('success' => false, 'mensaje' => $erroresMensaje);
}
$this->renderJSON($this->respuestaJSON);
}
How will be the test method? something like this?
public function actionCreateTest(){
$model = new Model;
$this->asserttrue($model->save());
}
write functional tests for testing controllers functionality instead of unit tests,also
the thing that you are asserting here
$this->assertEquals(true,$controller->actionCreate());
if the outcome of $controller->actionCreate() is the value true, which is not!
you are $this->renderPartial() in that and returning nothing, so that statement will never be true.
I keep fumbling over this - how do I mock a model that extends form Eloquent in Laravel 4 for my unit test?
I keep getting the following error w/ my current way
ErrorException: Trying to get property of non-object
Example
use \Repository\Text\EloquentText;
use \Faker\Factory as Faker;
class EloquentTextTest extends TestCase {
public function setUp()
{
parent::setUp();
$stub = $this->getMock('Text');
$stub->expects($this->any())->method('save');
$this->_fixture = new EloquentText($stub);
}
/**
* #test
*/
public function createShouldCreateNewTextEntry()
{
$faker = Faker::Create();
$data = [
'title' => $faker->sentence,
'content' => $faker->text,
'level_id' => $faker->randomDigit,
'is_public' => $faker->numberBetween(0, 1),
'is_visible' => $faker->numberBetween(0, 1),
];
$text = $this->_fixture->create($data);
$this->assertEquals($data['title'], $text->title);
$this->assertEquals($data['content'], $text->content);
$this->assertEquals($data['level_id'], $text->level_id);
$this->assertEquals($data['is_public'], $text->is_public);
$this->assertEquals($data['is_visible'], $text->is_visible);
return $text;
}