Unable to save new "ActiveRecord" in database through Codeception - unit-testing

I have a simple Unit test in which I'm trying to create a new record in "orders" table . So when the test is run it throws an exception :
[yii\db\IntegrityException] SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'created_by' cannot be null
I guess this is due to the so called "BlamemableBehavior" trying to update the "created_by" column . So I tried to detach it and to manually pass the "created_by" value . Neither of both worked . Can you please help ?
<?php
namespace frontend\tests\unit\models;
// use common\fixtures\UserFixture;
use frontend\components\Payments;
use common\models\order\Order;
class RandomTest extends \Codeception\Test\Unit
{
/**
* #var \frontend\tests\UnitTester
*/
protected $tester;
protected $order;
public function _before(){
//this doesn't work
$this->order = $this->tester->haveRecord(Order::class,
[
'id' => 577,
'payment_type' => 4,
'status' => 1,
'amount' => 1,
'created_by' => 561,
'updated_by' => 561,
]);
}
public function testRandom(){
//this does not work either
/*$model = new Order;
$model->detachBehavior('BlameableBehavior');
$model->payment_type = 4;
$model->status = 1;
$model->amount = 1;
$model->created_by = 561;
$model->updated_by = 561;
$model->save();*/
}
}
The "Order" model :
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors[] = [
'class' => \common\components\behaviors\modelLog\ActiveRecordHistoryBehavior::className(),
'manager' => '\common\components\behaviors\modelLog\managers\DBManager',
'ignoreFields' => ['updated_at', 'created_at']
];
return $behaviors;
}
/**
* #inheritdoc
*/
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['paid'] = [
'status', 'invoice_number', 'paid_date', 'is_used', 'allocation'];
return $scenarios;
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['status', 'payment_type', 'invoice_reference', 'invoice_number'], 'integer'],
[['payment_type', 'amount', 'vat', 'total_amount', 'credit', 'invoice_reference'], 'required'],
[['amount', 'vat', 'total_amount', 'credit', 'discount'], 'number'],
[['reason'], 'trim'],
[['allocation'], 'string']
];
}

Related

using Cakephp Restful WS with primary key different from default 'id'

I want to create a Webservice in cakephp but the primary key is not id_supp but taken the default value id
this is the modal:
<?php
App::uses('AppModel', 'Model');
class Supplier extends AppModel {
var $primaryKey = 'id_supp';
this is the route
Router::mapResources(array('suppliers'));
and this is the view action
public function view($id) {
$supplier = $this->Supplier->findById($id);
$this->set(array(
'supplier' => $supplier,
'_serialize' => array('supplier')
));
}
The result when accessing the following url via GET
/suppliers/54f4dc83-0bd0-4fdd-ab8b-0a08ba3b5702.json
is:
{
"code": 500,
"url": "\/TN\/Back_rest\/suppliers\/54f4dc83-0bd0-4fdd-ab8b-0a08ba3b5702.json",
"name": "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Supplier.id' in 'where clause'",
"error": {
"errorInfo": [
"42S22",
1054,
"Unknown column 'Supplier.id' in 'where clause'"
],
"queryString": "SELECT `Supplier`.`id_supp`, `Supplier`.`company_name`, `Supplier`.`contact_name`, `Supplier`.`contact_title`, `Supplier`.`address`, `Supplier`.`postcode`, `Supplier`.`phone`, `Supplier`.`fax`, `Supplier`.`www`, `Supplier`.`active`, `Supplier`.`created`, `Supplier`.`modified` FROM `tn`.`suppliers` AS `Supplier` WHERE `Supplier`.`id` = '54f4dc83-0bd0-4fdd-ab8b-0a08ba3b5702' LIMIT 1"
}}
Because cakephp uses convention over configuration you should use id for your table primary id field. In your example you could find what you are looking for like this:
public function view($id = null) {
$supplier = $this->Supplier->find('first', array(
'conditions' => array(
'Supplier.id_supp' => $id
)
));
$this->set(array(
'supplier' => $supplier,
'_serialize' => array('supplier')
));
}
or like this:
public function view($id = null) {
$this->Supplier->primaryKey = $id;
$supplier = $this->Supplier->find('first');
$this->set(array(
'supplier' => $supplier,
'_serialize' => array('supplier')
));
}
or like this:
public function view($id = null) {
$supplier = $this->Supplier->findByIdSupp($id);
$this->set(array(
'supplier' => $supplier,
'_serialize' => array('supplier')
));
}
Choose what ever pleases you the most.

Anyone else having trouble getting custom handlers to wrok on D8 entities?

I'm starting out with Drupal 8 and until now I have been quite impressed with all the new functionality. However, I have been trying to write my own entity and I'm running into trouble:
This is the entity definition:
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace Drupal\entitytest\Entity;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Defines the Candidate Entity
*
* #ingroup entitytest
*
* #ContentEntityType(
* id="entitytest_AuditionCandidate",
* label=#Translation("Candidate"),
* base_table="candidate",
*
* handlers = {
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "list_builder" = "Drupal\entitytest\Entity\Controller\CandidateListBuilder",
* "form" = {
* "add" = "Drupal\Core\Entity\ContentEntityForm",
* "edit" = "Drupal\Core\Entity\ContentEntityForm",
* "delete" = "Drupal\EntityTest\Form\CandidateDeleteForm",
* },
* },
* admin_permission="administer candidates",
* entity_keys={
* "id"="id",
* "label"="lastname",
* },
* links = {
* "canonical" = "/AuditionCandidate/view/{entitytest_AuditionCandidate}",
* "edit-form" = "/AuditionCandidate/edit/{entitytest_AuditionCandidate}",
* },
* )
*/
class Candidate extends ContentEntityBase {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['id'] = BaseFieldDefinition::create('integer')
->setLabel(t('ID'))
->setDescription(t('The ID of the Contact entity.'))
->setReadOnly(TRUE);
$fields['lastname'] = BaseFieldDefinition::create('string')
->setLabel(t('Last Name'))
->setDescription(t('The name of the Contact entity.'))
->setSettings(array(
'default_value' => '',
'max_length' => 255,
'text_processing' => 0,
))
->setDisplayOptions('view', array(
'label' => 'above',
'type' => 'string',
'weight' => -6,
))
->setDisplayOptions('form', array(
'type' => 'string',
'weight' => -6,
))
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);
$fields['firstname'] = BaseFieldDefinition::create('string')
->setLabel(t('First Name'))
->setDescription(t('The name of the Contact entity.'))
->setSettings(array(
'default_value' => '',
'max_length' => 255,
'text_processing' => 0,
))
->setDisplayOptions('view', array(
'label' => 'above',
'type' => 'string',
'weight' => -6,
))
->setDisplayOptions('form', array(
'type' => 'string',
'weight' => -6,
))
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);
return $fields;
}
}
So I am trying to edit the Deleteform from this entity. I have created a file under /modules/custom/EntityTest/src/Form/CandidateFormDelete.php
The code in this file is as follows:
<?php
namespace Drupal\EntityTest\Form;
use Drupal\Core\Entity\ContentEntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
class CandidateDeleteForm extends ContentEntityConfirmFormBase {
public function getQuestion() {
return $this->t('Are you sure?');
}
public function getCancelUrl() {
return new Url('entity.entitytest_AuditionCandidate.collection');
}
public function getConfirmText() {
return $this->t('Delete');
}
}
I have also added a route for the delete form:
entity.entitytest_AuditionCandidate.delete_form:
path: 'AuditionCandidate/delete/{entitytest_AuditionCandidate}'
defaults:
_entity_form: entitytest_AuditionCandidate.delete
_title: 'Delete Candidate'
requirements:
_permission: 'administer candidates'
But when I try to open /AuditionCandidate/delete/1 I'm getting the following error message:
Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException: The "entitytest_AuditionCandidate" entity type did not specify a "delete" form class. in Drupal\Core\Entity\EntityManager->getFormObject() (line 309 of core/lib/Drupal/Core/Entity/EntityManager.php).
It just doesn't seem to make sense since I have definined a class for the deleteform.
Anyone who can see what I am missing? It's possibly just a typo but I have been staring at it for quite some time now and I just can't figure it out.
Matt.
Drupal 8 implements the PSR-4 standard for package-based PHP namespace autoloading.
In that case the name of your class file doesn't correspond to the name of the actual class used.
The file name should also be "CandidateDeleteForm.php" instead of "CandidateFormDelete"
That's the reason why you are getting that exception.
For more about that subject read:
https://www.drupal.org/node/2156625

phpunit mock expectation failed for method name is equal

I have the following subject to test:
class ReportTable_Renderer_Html_Decorator_AddRecord extends ReportTable_Renderer_Html_Decorator_CallParent
{
public function renderAddItem(ReportTable $table)
{
$newRow = array();
$masterIDColumn = $this->getMasterIDColumn();
if (!empty($masterIDColumn)) {
$newRow[$masterIDColumn] = $this->getOwner()->getMasterID();
}
foreach ($table->getColumns() as $name => $column) {
$newRow[$name] = '';
}
$newRow['id'] = '0';
if (!empty($newRow[$masterIDColumn])) $newRow['id'] .= '_' . $newRow[$masterIDColumn];
$newRow[$this->getColumn()] = $this->getText();
$this->getRowStyle()->getGroupStyles()->add('do_not_print grey');
return $this->getParent()->renderRowContent($table, $newRow);
}
and also this (indirect) parent class, whose functions I need to stub for the test
class ReportTable_Renderer_Html_Decorator_Base extends ReportTable_Renderer_Html
{
public function renderRowContent(ReportTable $table, array $row) {}
public function renderRowSetFooter(ReportTable $table) {}
}
My test:
public function testRenderRowSetFooter()
{
$table = new ReportTable('a','b');
$table->addColumn( new ReportTable_Column( 'one', 'one' ));
$table->addColumn( new ReportTable_Column( 'two', 'two' ));
$table->addColumn( new ReportTable_Column( 'three', 'three' ));
$testText = 'test text';
$parentFooterText = 'parent.parent';
$groupID = 234;
$addText = 'Add me. Add me now!';
$newRow = array('one' => $addText, 'two' => $groupID, 'three' => '', 'id' => 0 );
$parent = $this->getMock('ReportTable_Renderer_Html_Base', array( 'renderRowContent', 'renderRowSetFooter' ));
$parent->expects($this->any())->method('renderRowContent')->with($table, $newRow)->will($this->returnValue($testText));
$parent->expects($this->any())->method('renderRowSetFooter')->with($table)->will($this->returnValue($parentFooterText));
$subject = $this->getSubject($parent, array( 'text' => $addText, 'column' => 'one', 'masterIDColumn' => 'two' ));
$subject->getOwner()->setMasterID($groupID);
$this->assertEquals($parentFooterText . $testText, $subject->renderRowSetFooter($table));
}
I'm stuck with this error message which happens for both mocked functions:
PHPUnit_Framework_ExpectationFailedException : Expectation failed for method name is equal to <string:renderRowContent> when invoked zero or more times
Parameter 1 for invocation Herkt_ReportTable_Renderer_Html_Base::renderRowContent(Herkt_ReportTable Object (...), Array (...)) does not match expected value.
Failed asserting that two arrays are equal.
One of the arrays shows is $newRow, the other one obviously the resut of the function. But I did not add an assertEquals for these arrays? How does this come about and how can I fix my test?
Ok, figured it out. I inherited this test and am adapting it to changed functionality. What happens is that because of the mock functions, the actual testing happens by passing $newRow into the mock function renderRowContent
My test failed because I didn't adapt the expected parameters to the change in my tested function
it should be:
$newRow = array(
'masterColumn' => $groupID,
'one' => $addText,
'two' => '',
'three' => '',
'id' => '0_234'
);

ZF2 - set selected value on Select Element

I've a problem with dropdown list with Zend Framework 2 & Doctrine.
I would put the "selected" attribute on my dropdown list but all options pass to selected
My code :
Controller :
public function editAction()
{
// get error message during addAction
$this->layout()->setVariable("messageError", $this->flashMessenger()->getErrorMessages());
$auth = $this->getAuthService();
if ($auth->hasIdentity()){
$builder = new AnnotationBuilder();
// Get id of StaticContent
$id = (int)$this->getEvent()->getRouteMatch()->getParam('id');
if (!$id) {
$this->flashMessenger()->addErrorMessage("Aucun plan choisi !");
return $this->redirect()->toRoute('admin/plans');
}
$plan = $this->getEntityManager()->getRepository("Admin\Entity\Plan")->find((int)$id);
$form = $builder->createForm($plan);
// Find options for Localite list (<select>)
$localites = $this->getEntityManager()->getRepository("Admin\Entity\Localite")->getArrayOfAll();
$form->get('localiteid')->setValueOptions($localites);
$form->get('localiteid')->setValue("{$plan->getLocaliteid()->getId()}");
// Find options for TypePlan list (<select>)
$typesPlan = $this->getEntityManager()->getRepository("Admin\Entity\TypePlan")->getArrayOfAll();
$form->get('typeid')->setValueOptions($typesPlan);
$form->get('typeid')->setValue("{$plan->getTypeid()->getId()}");
// Options for Statut list (<select>)
$form->get('statut')->setValueOptions(array('projet'=>'Projet', 'valide'=>'Validé'));
$form->get('statut')->setValue($plan->getStatut());
$form->setBindOnValidate(false);
$form->bind($plan);
$form->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Modifier',
'id' => 'submitbutton',
'class' => "btn btn-primary"
),
));
$request = $this->getRequest();
if ($request->isPost()) {
[...]
}
}
With
$localites = $this->getEntityManager()->getRepository("Admin\Entity\Localite")->getArrayOfAll();
$form->get('localiteid')->setValueOptions($localites);
i populate my dropdown correctly, normally with
$form->get('localiteid')->setValue("{$plan->getLocaliteid()->getId()}");
just set "selected" on option defined by :
$plan->getLocaliteid()->getId()
So why all options are selected in my dropdown ?!
Information : It's the same for typeId but no Statut
It's probably not working because of the curly braces. According to the PHP documentation
Using single curly braces ({}) will not work for accessing the return values of functions or methods or the values of class constants or static class variables.
This is also unnecessary when using setValue. ZF2 will convert it to a string when formatting it in the view.
When you create the arrays to pass to setValueOptions() you should make it an associative array of arrays with the following values:
$form->get('select')->setValueOptions(array(
'field' => array(
'value' => 'value_of_the_option',
'label' => 'what is displayed',
'selected' => true,
),
));
Which ever of the fields has the selected option set to true will be the default selection in the form element.
Personally i don't know if getArrayOfAll() such function exists, i assume that you are correctly passing array to FORM,
I think you should be doing something like this to set value.
$form->get('localiteid')->setValue($plan->getLocaliteid()->getId());
But Since you are populating DROP down i guess this approach will not work best with Drop Down. You need to do something like this
$form->get('localiteid')->setAttributes(array('value'=>$plan->getLocaliteid()->getId(),'selected'=>true));
I've found a bug ?!
$plan = $this->getEntityManager()->getRepository("Admin\Entity\Plan")->find((int)$id);
$idLocalite = 18;//(int)$plan->getLocaliteid()->getId();
$idTypePlan = 2;//(int)$plan->getTypeid()->getId();
When i'm using $plan->getLocaliteid()->getId(); or $plan->getTypeid()->getId() to pass parameter into Repository method getArrayOfAll($idLocalite)
LocaliteRepository.php :
class LocaliteRepository extends EntityRepository {
public function getArrayOfAll($currentLocaliteId) {
$result = $this->_em->createQuery("SELECT l.nom, l.localiteid FROM Admin\Entity\Localite l ORDER BY l.nom")->getArrayResult();
$localite = array();
foreach($result as $loc) {
if ($currentLocaliteId == $loc['localiteid']) {
$localite[$loc['localiteid']] = array(
'value' => $loc['localiteid'],
'label' => $loc['nom'],
'selected' => true,
);
} else {
$localite[$loc['localiteid']] = array(
'value' => $loc['localiteid'],
'label' => $loc['nom'],
'selected' => false
);
//$localite[$loc['localiteid']] = $loc['nom'];
}
}
return $localite;
}
}
So, if i'm using $idLocalite = 18 instead of $idLocalite = (int)$plan->getLocaliteid()->getId() only wanted option are selected. Why ?!

using zend test dbadapter with zend db table abstract

Has anyone been able to to use Zend_Test_DbAdapter with Zend_Db_Table_Abstract?
I'm trying to test a Model I created that extends Zend_Db_Table_Abstract and I get an exception regarding the primary key not being set if I use a Zend_Test_DbAdapter (other adapters like mysql or sqlite) work fine.
class Model_Category extends Zend_Db_Table_Abstract
{
protected $_name = 'categories';
protected $_dependentTables = array('Model_Video');
public function getMap()
{
$map = array();
$rows = $this->fetchAll();
foreach($rows as $row)
{
$map[$row->id] = $row->name;
}
return $map;
}
}
Snippet from a PHPUnit test class:
protected function setUp()
{
$adapter = new Zend_Test_DbAdapter();
$stmt = Zend_Test_DbStatement::createSelectStatement(array(
array('id' => 1, 'name' => 'pranks'),
array('id' => 2, 'name' => 'physical_feats'),
array('id' => 3, 'name' => 'art'),
array('id' => 4, 'name' => 'cute'),
array('id' => 5, 'name' => 'philanthropy')
));
$adapter->appendStatementToStack($stmt);
$this->fixture = new Model_Category($adapter);
}
Exceptions are thrown when exercising the Model's methods:
public function testGetMap()
{
$expected = array(
'1' => 'pranks',
'2' => 'physical_feats',
'3' => 'art',
'4' => 'cute',
'5' => 'philanthropy'
);
$actual = $this->fixture->getMap();
$this->assertEquals($expected, $actual);
}
Results in:
Model_CategoryTest::testGetMap()
Zend_Db_Table_Exception: A table must have a primary key, but none was found
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:876
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:969
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:100
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:78
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1005
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1303
application/models/Category.php:35
tests/unit/application/models/CategoryTest.php:90
Forcing a primary key does not work either:
protected function setUp()
{
$adapter = new Zend_Test_DbAdapter();
$stmt = Zend_Test_DbStatement::createSelectStatement(array(
array('id' => 1, 'name' => 'pranks'),
array('id' => 2, 'name' => 'physical_feats'),
array('id' => 3, 'name' => 'art'),
array('id' => 4, 'name' => 'cute'),
array('id' => 5, 'name' => 'philanthropy')
));
$adapter->appendStatementToStack($stmt);
$this->fixture = new Model_Category(array(
'db' => $adapter,
'primary' => 'id'
));
}
Executing the same unit test, from above results in:
Model_CategoryTest::testGetMap()
Zend_Db_Table_Exception: Primary key column(s) (id) are not columns in this table ()
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:888
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:969
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:100
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:78
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1005
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1303
application/models/Category.php:35
tests/unit/application/models/CategoryTest.php:93
You can define the primary key by doing the following on your Zend_Test_DbAdapter instance:
$adapter = new Zend_Test_DbAdapter();
$adapter->setDescribeTable('table_name', array('column_name' =>
array(
'SCHEMA_NAME' => 'schema_name',
'TABLE_NAME' => 'table_name'
'COLUMN_NAME' => 'column_name',
'PRIMARY' => true
)
));
And then transposing table_name, column_name and schema_name with the values from your implementation. You would need to do this for every table you are interacting with in the class under test.
The reason you are getting the exception Zend_Db_Table_Exception: A table must have a primary key, but none was found is because all tables that use Zend_Db_Table must have a primary key defined. When you go to use the table, since a primary key was not defined in your DbTable class, Zend_Db attempts to determine the table's primary key by examining the table's properties from the information schema. It sees your table doesn't have a primary key and fails.
From the manual:
If you don't specify the primary key, Zend_Db_Table_Abstract tries to
discover the primary key based on the information provided by the
describeTable() method.
Note: Every table class must know which columns can be used to address
rows uniquely. If no primary key columns are specified in the table
class definition or the table constructor arguments, or discovered in
the table metadata provided by **describeTable(), then the table cannot
be used with Zend_Db_Table.
Trying to force the primary key isn't working because it looks like your table doesn't have a column called id which you are specifying as the primary key.
The solution would be to add a primary key to the table you are trying to use.
In your model class that extends Zend_Db_Table_Abstract you can specify a primary key that isn't ID using protected $_primary = 'primary_column';