in my code i want save a category in db and check whether it has been saved or not but the code seems to not work. my data $category1 does not get passed through the call function. if I use create instead of make in factory then the entire command becomes redundant. I want test basically create operation working or not.
Test:
/**
* #test
*/
public function save_method_saves_a_category()
{
$category1 = Category::factory()->make([
'id' => '1',
]);
$this->call('POST','/submitcategory',[$category1]);
$this->assertDatabaseHas('categories', [
'id' => '1',
'name' => $category1->name
]);
}
Route:
Route::post('/submitcategory', [CategoryController::class,'save']);
Controller:
public function save(Request $r){
$cat = new Category;
$cat->name=$r->name;
$cat->parent_id=$r->parent_id;
$cat->description=$r->description;
$cat->slug=$r->slug;
$cat->save();
return redirect('/admin/categories');
}
Related
I need to render a custom form which is created using Drupal\Core\Form\FormBase and Drupal\Core\Form\FormStateInterface through a controller in custom Drupal 8 module. Is there any guidence or reference to follow to do this?
Actually I tried to render form directly and through a controller. But both ways are not working. Only render the submit button. I refer the drupal 8 documentation also. But I couldn't find a solution for this. Please be kind enough to find my coding samples below. If there are anything wrong. Please correct me.
my_module.routing.yml
partner.content:
path: '/partner'
defaults:
_controller: '\Drupal\partner\Controller\PartnerController::add'
_title: 'Add Partner'
requirements:
_permission: 'access content'
partner.addform:
path: '/partner/add'
defaults:
_form: '\Drupal\partner\Form\AddForm'
_title: 'Add Partner'
requirements:
_permission: 'access content'
AddForm.php
namespace Drupal\my_module\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class AddForm extends FormBase
{
/**
* Returns form id
*
* #return string
*/
public function getFormId(): string
{
return 'my_module_add_form';
}
/**
* Build form array
*
* #param array $form
* #param FormStateInterface $formState
* #return array
*/
public function buildForm(array $form, FormStateInterface $form_state): array
{
// First name
$form['first_name'] = [
'#type' => 'textField',
'#title' => t('First Name'),
'#required' => true,
];
// Other input fields...
$form['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Save Changes'),
'#button_type' => 'primary',
);
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state) {}
public function submitForm(array &$form, FormStateInterface $form_state) {}
}
MyModuleController.php
<?php
namespace Drupal\my_module\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\my_module\Form\AddForm;
class MyModuleController extends ControllerBase
{
public function add()
{
$addForm = new AddForm();
$form = \Drupal::formBuilder()->getForm($addForm);
return [
'#theme' => 'form_my_module_add',
'#form' => $form,
];
}
}
Happy to find out the solution with Hemantha Dhanushka on my comment.
To make it clear this question has a correct answer, here I past the validated comment.
I would recommend you to use the first approach (using routing::_form instead
of Controller). Also, it seems you use the wrong #type for your
first_name field. Try textfield instead of textField.
Also, for people who want to go further, here are some links to implement a proper
routing::_form approach to expose a form as a page instead of using a Controller: https://www.valuebound.com/resources/blog/step-by-step-method-to-create-a-custom-form-in-drupal-8.
For people looking for more help about existing Form Element Reference (textfield, checkboxes, entity_autocomplete, ...) here is an excellent up-to-date article https://drupalize.me/tutorial/form-element-reference?p=2766
You can use buildForm() method for it. Check below code example:
public function add()
{
$form_state = new Drupal\Core\Form\FormState();
$form_state->setRebuild();
$form = \Drupal::formBuilder()->buildForm('Drupal\my_module\Form\AddForm', $form_state);
return [
'#theme' => 'form_my_module_add',
'#form' => $form,
];
}
Reference: https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Form!FormBuilder.php/function/FormBuilder::getForm/8.2.x
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');
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;
}
I'm new to CakePHP, and I just started writing my first tests. Usually doing Ruby on Rails, my approach to testing a Controller::create action would be to call the create action, and then comparing the number of models before and after that call, making sure it increased by one.
Would anyone test this any other way?
Is there an easy (builtin) way to access models from a ControllerTest in CakePHP? I couldn't find anything in the source, and accessing it through the Controller seems wrong.
I ended up doing something like this:
class AbstractControllerTestCase extends ControllerTestCase {
/**
* Load models, to be used like $this->DummyModel->[...]
* #param array
*/
public function loadModels() {
$models = func_get_args();
foreach ($models as $modelClass) {
$name = $modelClass . 'Model';
if(!isset($this->{$name})) {
$this->{$name} = ClassRegistry::init(array(
'class' => $modelClass, 'alias' => $modelClass
));
}
}
}
}
Then my tests inherit from AbstractControllerTestCase, call $this->loadModels('User'); in setUp and can do something like this in the test:
$countBefore = $this->UserModel->find('count');
// call the action with POST params
$countAfter = $this->UserModel->find('count');
$this->assertEquals($countAfter, $countBefore + 1);
Note that I'm new to CakePHP but came here with this question. Here's what I ended up doing.
I got my idea from #amiuhle, but I just do it manually in setUp, like how they mention in the model tests at http://book.cakephp.org/2.0/en/development/testing.html.
public function setUp() {
$this->Signup = ClassRegistry::init('Signup');
}
public function testMyTestXYZ() {
$data = array('first_name' => 'name');
$countBefore = $this->Signup->find('count');
$result = $this->testAction('/signups/add',
array(
'data' => array(
'Signup' => $data)
)
);
$countAfter = $this->Signup->find('count');
$this->assertEquals($countAfter, $countBefore + 1);
}
I am not sure why it is necessary to test how many times a model is called or instantiated from the controller action.
So, if I was testing Controller::create... my ControllerTest would contain something like:
testCreate(){
$result = $this->testAction('/controller/create');
if(!strpos($result,'form')){
$this->assertFalse(true);
}
$data = array(
'Article' => array(
'user_id' => 1,
'published' => 1,
'slug' => 'new-article',
'title' => 'New Article',
'body' => 'New Body'
)
);
$result = $this->testAction(
'/controller/create',
array('data' => $data, 'method' => 'post')
);
if(!strpos($result,'Record has been successfully created')){
$this->assertFalse(true);
}
}
The main things you want to test for is whether you are getting the right output for the input. And you can use xDebug profiler to easily find out what classes get instnantiated in a particular action and even how many times. There is no need to test for that manually!
how to test such a method:
public function add() {
if (!empty($this->request->data)) {
$this->Contest->create();
if ($this->Contest->saveAll($this->request->data)) {
$contestStage['name'] = 'First - ' . $this->request->data['Contest']['name'];
$contestStage['contest_id'] = $this->Contest->id;
if ($this->Contest->ContestStage->save($contestStage)) {
$this->setMessage(__ADD_OK, 'Konkurs');
$this->redirect(array(
'action' => 'view',
$this->Contest->id
));
} else {
$this->setMessage(__ADD_ERROR, 'Konkurs');
}
} else {
$this->setMessage(__ADD_ERROR, 'Konkurs');
}
}
}
my test method:
public function testAdd() {
$this->generateWithAuth(self::ADMIN); // genereting controller here
$url = $this->getUrl('add');
$options2 = array(
'method' => 'post',
'data' => array(
'Contest' => array(
'id' => 3,
'owner_id' => 1,
'name' => 'Testing',
'created' => '2012-11-16 12:02:33.946',
),
),
);
$this->testAction($url, $options2);
$this->assertArrayHasKey('Location', $this->headers, 'No redirection');
$this->assertEquals($this->Contest->hasAny(array('Contest.name' => 'Testing')), true);
$messages = Set::extract('{flash}.message', CakeSession::read('Message'));
}
what i receive is
PDOEXCEPTION
SQLSTATE[23505]: Unique violation: 7 BŁĄD: double key value violates a constraint
uniqueness "contest_stages_pkey" DETAIL: Key (id)=(1) alredy exists.
Because it's true i have a contestStage with id=1
why its not using next one ;<
Its kinda strange that Cakephp does not document how to test that well.
The problem is that your inserting the Contest id twice. You should make sure that the db that your using has a test prefix (or whatever you like) and clear the test tables.
As an alternative, you could use fixtures instead. The data produces a much better test case as it pre populates the data for you so that you know whats in the db at any time. Still make sure to use a prefix, I made the mistake once of not doing that and it blew away my entire db every time
Good luck