I'm just starting out with TDD and I'm trying to test validation in a model. Currently, all my tests pass, but I believe that they shouldn't. The testBodyIsRequiredToValidate() should fail since I have not added it to the rules array. When I remove the title required from the rules array, all my tests fail as expected. However, adding title required causes them all to pass.
My Tests:
class PostTest extends TestCase {
public function testTitleIsRequiredToValidate()
{
$post = new Post;
$post->body = 'foobar';
$this->assertFalse($post->validate(compact('post')));
}
public function testBodyIsRequiredToValidate()
{
$post = new Post;
$post->title = 'foobar';
$this->assertFalse($post->validate(compact('post')));
}
}
My Model:
class Post extends Eloquent {
public function validate($input)
{
$rules = array(
'title' => 'required'
);
$v = Validator::make($input, $rules);
if ($v->passes()) {
return true;
} else {
return false;
}
}
}
This happens because your validation is wrong. You pass the model itself to the validator but you should pass an associative array.
When you delete title from the rules you pass an empty rule, so the validation will be always true, thats why all your test fail.
When you add title to your rules, the validations will be always false hence all your tests pass, because the validator's first parameter doesn't have an array element called title.
One way to correct this is to change your validation line to this:
$v = Validator::make(array('title' => $input['post']->title), $rules);
Here you get the $post from your $input array, because compact() will put it to an array with the key of the variable name. Then you access the title property of the object and make this to the value of the title key so the validator will be able to find and validate it.
I have to mention that your validation technique is kinda bad for me. You pass the object in an array to a function of the same object. Why don't you get the object in the function?
I would write the validator function like this:
public function validate() {
$rules = array(
'title' => 'required'
);
$v = Validator::make($this->toArray(), $rules);
return $v->passes();
}
So I could call the function like this in the tests:
$this->assertFalse($post->validate());
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'm developing a subclass of DropdownField and I'm trying to couple it with a corresponding DrodownFieldData.ss template without a success.
I flushed the cache repeatedly.
I deleted the cache folder on my localhost (XAMPP)
I moved the template around to various locations of my 'simple' theme: /simple/templates, /simple/templates/forms, /simple/templates/Includes
As you can see, the template name does not have an underscore character which has been reported to cause problems
I'm calling it as in:
return $this->customise($properties)->renderWith('DropdownFieldData');
Do you have any other ideas that I could give a try?
This is the code. It's basically a copy of DropdownField, skimmed down to the Field method.
<?php
class DropdownFieldData extends DropdownField {
public function Field($properties = array()) {
$source = $this->getSource();
$options = array();
if($source) {
// SQLMap needs this to add an empty value to the options
if(is_object($source) && $this->emptyString) {
$options[] = new ArrayData(array(
'Value' => '',
'Title' => $this->emptyString,
));
}
foreach($source as $value => $title) {
$selected = false;
if($value === '' && ($this->value === '' || $this->value === null)) {
$selected = true;
} else {
// check against value, fallback to a type check comparison when !value
if($value) {
$selected = ($value == $this->value);
} else {
$selected = ($value === $this->value) || (((string) $value) === ((string) $this->value));
}
$this->isSelected = $selected;
}
$disabled = false;
if(in_array($value, $this->disabledItems) && $title != $this->emptyString ){
$disabled = 'disabled';
}
$options[] = new ArrayData(array(
'Title' => $title,
'Value' => $value,
'Selected' => $selected,
'Disabled' => $disabled,
));
}
}
$properties = array_merge($properties, array('Options' => new ArrayList($options)));
return $this->customise($properties)->renderWith('DropdownFieldData');
// return parent::Field($properties);
}
}
I had a similar problem that was the result of timing (there was a template loaded later that replaced my own customisation, but only when using the default form template). The fix was making sure the subclassed form field had its own version of the FormField Holder method. EG:
public function FieldHolder($properties = array()) {
$obj = $properties ? $this->customise($properties) : $this;
return $obj->renderWith($this->getTemplates());
}
The template should be in templates/forms/CustomField.ss. I don't think it should matter if this is in your theme folder, in mysite, or in a module folder.
You could try the following:
rename your template DropdownFieldData_holder.ss
move DropdownFieldData_holder.ss into mysite/templates/DropdownFieldData_holder.ss
use the setFieldHolderTemplate method on your field, ie $this->setFieldHolderTemplate('DropdownFieldData_holder');
You'll also find that the fields are rendered using two templates, one of them being suffixed with '_holder', which acts as a wrapper, while the other one renders the field itself, so depending on how you want to customise your field, you might have to create both.
Have a look at the FormField class to get a better understanding on how fields are rendered, as they use a slightly different mechanism than page types
The key was to keep the template in [yourmodule]/templates, rather than in any theme's location.
This question is one in a series I seem to be generating as I slowly pick my way through learning testing.
I have a book model and a ticketaudit model. They have a relationship one to many. When a book is created a function should also create a range of tickets (for audit).
I want my test to make sure the ticketAudit model is being created and the association being made using the eloquent ORM within laravel.
my class so far:
Class TicketCreator implements TicketCreatorInterface {
protected $ticket;
public function __construct(TicketAudit $ticketAudit)
{
//dd($ticketAudit);
$this->ticket = $ticketAudit;
}
public function createTicket($input, $book) {
$counter = $input['start'];
while($counter <= $input['end']) {
$ticketDetails = array(
'ticketnumber'=>$counter,
'status'=>'unused',
'active'=>1
);
$this->ticket->create($ticketDetails)->save();
$this->ticket->book()->associate($book)->save();
$counter = $counter+1;
}
return $counter;
}
}
and my attempts at a test:
public function testCreateCreatesTickets() {
//arrange
$book = FactoryMuff::create('Book');
$aTicket = FactoryMuff::create('TicketAudit');
$ticketAudit = new TicketAudit;
$ticketCreator = new TicketCreator($ticketAudit);
//act
$response = $ticketCreator->createTicket(array('start'=>1000, 'end'=>1001), $book);
// Assert...
$this->assertEquals(true, $response);
}
However when I run this test I get the error:
Integrity constraint violation: 19 ticket_audits.ticketnumber may not be NULL
For some reason the model is not being created with the values I pass to it. I've checked in the function that the object exists and also the values are being created correctly in the array but it doesnt work.
Is this unique to testing?
I am creating an sqlite in memory database for this test.
Any help appreciated
Crikey this decision to start testing is a bit of a nightmare
Thanks to Manuel's request to post the TicketAudit class I noticed my model extended Eloquent. I had recently added Ardent and should have extended Ardent so the error lay in the model!
Revised corrected model :
use LaravelBook\Ardent\Ardent;
class TicketAudit extends Ardent {
protected $guarded = array();
public $autoHydrateEntityFromInput = true;
public $autoPurgeRedundantAttributes = true;
public static $rules = array(
'status' => 'required',
'ticketnumber' => 'required'
);
public static $factory = array(
'ticketnumber' => '1000',
'status' => 'unused',
);
public function book() {
return $this->belongsTo('Book');
}
}
Thank you for the sign post
i am writing webservice in symfony2 but i facing some problem regarding the output ,as it is giving blank output.
class DefaultController extends Controller {
/**
*
* #Route("/webservices/activity/{id}", name="user_json_activity")
* #Method("get")
*/
public function activityAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$list = $em->getRepository('FitugowebserviceBundle:activity')->findOneById($id);
$r_array = $this->routes2Array($list);
$r = array('activity' => $r_array);
return new Response(json_encode($r));
}
private function routes2Array($routes) {
$points_array = array();
foreach ($routes as $route) {
$r_array = array('activity' => $route->getActivity(),
'icon' => $route->getIcon());
$points_array[] = $r_array;
}
return $points_array;
}
}
When i try to fetch data for id=1 http://domain.org/fitugo/web/app_dev.php/webservices/activity/1 it is giving output as follows
{"activity":[]}
It look very strange that you want get array with findOneById method. The first thing I suggest to add a check that the entity founded by id exist. Then look that findOneById returns and check your controller logic.
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!