My problem:
How my I test some MVC part with Pest in CakePHP, for example:
When I´m use PHPUnit, I´ll type:
public class ItemTest extends TestCase
And it will show what thing that I wanna test, but how do that with Pest?
(Obs.: I´m really new on tests, so I accept anything that will make me improve :) )
After you installed Pest you just have to follow its syntax and documentation, removing all the OOP code and just using test() or it(). The file name will be the same. This is an example from CakePHP docs:
class ArticlesTableTest extends TestCase
{
public $fixtures = ['app.Articles'];
public function setUp()
{
parent::setUp();
$this->Articles = TableRegistry::getTableLocator()->get('Articles');
}
public function testFindPublished()
{
$query = $this->Articles->find('published');
$this->assertInstanceOf('Cake\ORM\Query', $query);
$result = $query->enableHydration(false)->toArray();
$expected = [
['id' => 1, 'title' => 'First Article'],
['id' => 2, 'title' => 'Second Article'],
['id' => 3, 'title' => 'Third Article']
];
$this->assertEquals($expected, $result);
}
}
With Pest, it will become:
beforeEach(function () {
$this->Articles = TableRegistry::getTableLocator()->get('Articles');
});
test('findPublished', function () {
$query = $this->Articles->find('published');
expect($query)->toBeInstanceOf('Cake\ORM\Query');
$result = $query->enableHydration(false)->toArray();
$expected = [
['id' => 1, 'title' => 'First Article'],
['id' => 2, 'title' => 'Second Article'],
['id' => 3, 'title' => 'Third Article']
];
expect($result)->toBe($expected);
});
Note that toBeInstanceOf() and toBe() are part of the available Pest expectations.
Finally, instead of running your test suite with $ ./vendor/bin/phpunit, you will run it with $ ./vendor/bin/pest.
I'm porting code from ZF2 to ZF3.
In ZF2 when I create a form via FormElementManager I can access the servicelocator on the init method and configure some stuff like this:
public function init()
{
$this->serviceLocator = $this->getFormFactory()->getFormElementManager()->getServiceLocator();
$this->translator = $this->serviceLocator->get('translator');
}
This is convenient in very large applications. In fact all my forms inherit from a BaseForm class.
In ZF3 this is bad pratic and serviceLocator are deprecated.
Which is the best way to get the same result ?
One way is to inject every form in the ControllerFactory or ServiceFactory with the stuff needed but this is very tedious.
Any help is appreciate.
First of, you should not have the ServiceManager and/or childs of it (like the FormElementManager) available in your Form objects.
Using the Factory pattern, you should create fully functional, stand-alone Form, Fieldset and InputFilter objects.
There will definitely be some tedious work, as you put it, but you need only do it once.
Let's say you want to create a Location. A Location consists of a name property and a OneToOne unidirectional Address reference. This creates the following needs:
LocationForm (-InputFilter)
LocationFieldset (-InputFilter)
AddressFieldset (-InputFilter)
Config for the above
Factory for each of the 6 classes above
In this answer I'll mash everything down to bare minimums and use classes and examples from my own repositories, so for full code you can go here and for examples here.
After the creation of the classes themselves, I'll show you the config you need for this use case and the Factories which tie all of it together.
AbstractFieldset
abstract class AbstractFieldset extends Fieldset
{
public function init()
{
$this->add(
[
'name' => 'id',
'type' => Hidden::class,
'required' => false,
]
);
}
}
AbstractInputFilter
abstract class AbstractFieldsetInputFilter extends AbstractInputFilter
{
public function init()
{
$this->add([
'name' => 'id',
'required' => false,
'filters' => [
['name' => ToInt::class],
],
'validators' => [
['name' => IsInt::class],
],
]);
}
}
AddressFieldset
class AddressFieldset extends AbstractFieldset
{
public function init()
{
parent::init();
$this->add([
'name' => 'street',
'required' => true,
'type' => Text::class,
'options' => [
'label' => 'Address',
],
]);
}
}
AddressInputFilter
class AddressFieldsetInputFilter extends AbstractFieldsetInputFilter
{
public function init()
{
parent::init();
$this->add([
'name' => 'street',
'required' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
[
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING,
],
],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 3,
'max' => 255,
],
],
],
]);
}
}
So far, easy. Now, we need to create the LocationFieldset and LocationFieldsetInputFilter. These will make use of the Address(-Fieldset) classes.
LocationFieldset
class LocationFieldset extends AbstractFieldset
{
public function init()
{
parent::init();
$this->add([
'name' => 'name',
'required' => true,
'type' => Text::class,
'options' => [
'label' => 'Name',
],
]);
$this->add([
'type' => AddressFieldset::class,
'name' => 'address',
'required' => true,
'options' => [
'use_as_base_fieldset' => false,
'label' => 'Address',
],
]);
}
}
LocationFieldsetInputFilter
class LocationFieldsetInputFilter extends AbstractFieldsetInputFilter
{
/**
* #var AddressFieldsetInputFilter
*/
protected $addressFieldsetInputFilter;
public function __construct(AddressFieldsetInputFilter $addressFieldsetInputFilter)
{
$this->addressFieldsetInputFilter = $addressFieldsetInputFilter;
}
public function init()
{
parent::init();
$this->add($this->addressFieldsetInputFilter, 'address');
$this->add(
[
'name' => 'name',
'required' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
[
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING,
],
],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 3,
'max' => 255,
],
],
],
]
);
}
}
Ok, so that's not very exciting yet. Do note, the LocationFieldset uses the AddressFieldset as a type. Instead, in the InputFilter class a full fledged class object (an InputFilter instance) is expected.
So, the Form. I also use an AbstractForm (BaseForm in your case) to handle a few defaults. In my complete one (in linked repo), there's a bit more, but for here this'll suffice. This adds CSRF protection to the Form and adds a submit button if the form does not have one. This only gets done if the Form class does not have either one when you call the init, so you can override these settings.
AbstractForm
abstract class AbstractForm extends \Zend\Form\Form implements InputFilterAwareInterface
{
protected $csrfTimeout = 900; // 15 minutes
public function __construct($name = null, $options = [])
{
$csrfName = null;
if (isset($options['csrfCorrector'])) {
$csrfName = $options['csrfCorrector'];
unset($options['csrfCorrector']);
}
parent::__construct($name, $options);
if ($csrfName === null) {
$csrfName = 'csrf';
}
$this->addElementCsrf($csrfName);
}
public function init()
{
if (!$this->has('submit')) {
$this->addSubmitButton();
}
}
public function addSubmitButton($value = 'Save', array $classes = null)
{
$this->add([
'name' => 'submit',
'type' => Submit::class,
'attributes' => [
'value' => $value,
'class' => (!is_null($classes) ? join (' ', $classes) : 'btn btn-primary'),
],
]);
}
public function get($elementOrFieldset)
{
if ($elementOrFieldset === 'csrf') {
// Find CSRF element
foreach ($this->elements as $formElement) {
if ($formElement instanceof Csrf) {
return $formElement;
}
}
}
return parent::get($elementOrFieldset);
}
protected function addElementCsrf($csrfName = 'csrf')
{
$this->add([
'type' => Csrf::class,
'name' => 'csrf',
'options' => [
'csrf_options' => [
'timeout' => $this->csrfTimeout,
],
],
]);
}
}
LocationForm
class LocationForm extends AbstractForm
{
public function init()
{
$this->add([
'name' => 'location',
'type' => LocationFieldset::class,
'options' => [
'use_as_base_fieldset' => true,
],
]);
parent::init();
}
}
Now we have everything to make the Form. We still need the validation. Let's create these now:
AddressFieldsetInputFilter
class AddressFieldsetInputFilter extends AbstractFieldsetInputFilter
{
public function init()
{
parent::init();
$this->add([
'name' => 'street',
'required' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
[
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING,
],
],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 3,
'max' => 255,
],
],
],
]);
}
}
LocationFieldsetInputFilter
class LocationFieldsetInputFilter extends AbstractFieldsetInputFilter
{
protected $addressFieldsetInputFilter;
public function __construct(AddressFieldsetInputFilter $addressFieldsetInputFilter)
{
$this->addressFieldsetInputFilter = $addressFieldsetInputFilter;
}
public function init()
{
parent::init();
$this->add($this->addressFieldsetInputFilter, 'address');
$this->add(
[
'name' => 'name',
'required' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
[
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING,
],
],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 3,
'max' => 255,
],
],
],
]
);
}
}
LocationFormInputFilter
class LocationFormInputFilter extends AbstractFormInputFilter
{
/** #var LocationFieldsetInputFilter */
protected $locationFieldsetInputFilter;
public function __construct(LocationFieldsetInputFilter $filter)
{
$this->locationFieldsetInputFilter = $filter
}
public function init()
{
$this->add($this->locationFieldsetInputFilter, 'location');
parent::init();
}
}
Right, that's all of the classes themselves. Do you see how they'll be nested together? This creates re-usable components, which is why I said you'll need to do this only once. Next time you need an Address or a Location, you just make sure to load the AddressFieldset and set the InputFilter in the Factory. The latter, setting the right InputFilter, is done via Setter Injection the Factories. Shown below.
AbstractFieldsetFactory
abstract class AbstractFieldsetFactory implements FactoryInterface
{
/**
* #var string
*/
protected $name;
/**
* #var string
*/
protected $fieldset;
/**
* #var string
*/
protected $fieldsetName;
/**
* #var string
*/
protected $fieldsetObject;
public function __construct($fieldset, $name, $fieldsetObject)
{
$this->fieldset = $fieldset;
$this->fieldsetName = $name;
$this->fieldsetObject = $fieldsetObject;
$this->hydrator = new Reflection(); // Replace this with your own preference, either Reflection of ZF or maybe the Doctrine EntityManager...
}
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$fieldset = $this->fieldset;
$fieldsetObject = $this->fieldsetObject;
/** #var AbstractFieldset $fieldset */
$fieldset = new $fieldset($this->hydrator, $this->name ?: $this->fieldsetName);
$fieldset->setHydrator(
new DoctrineObject($this->hydrator)
);
$fieldset->setObject(new $fieldsetObject());
return $fieldset;
}
}
AddressFieldsetFactory
class AddressFieldsetFactory extends AbstractFieldsetFactory
{
public function __construct()
{
parent::__construct(AddressFieldset::class, 'address', Address::class);
}
}
LocationFieldsetFactory
class LocationFieldsetFactory extends AbstractFieldsetFactory
{
public function __construct()
{
parent::__construct(LocationFieldset::class, 'location', Location::class);
}
}
AbstractFieldsetInputFilterFactory
abstract class AbstractFieldsetInputFilterFactory implements FactoryInterface
{
/**
* #var ContainerInterface
*/
protected $container;
/**
* #var HydratorInterface
*/
protected $hydrator;
/**
* #var InputFilterPluginManager
*/
protected $inputFilterManager;
/**
* Use this function to setup the basic requirements commonly reused.
*
* #param ContainerInterface $container
* #param string|null $className
* #throws \Psr\Container\ContainerExceptionInterface
* #throws \Psr\Container\NotFoundExceptionInterface
*/
public function setupRequirements(ContainerInterface $container, $className = null)
{
$this->inputFilterManager = $container->get(InputFilterPluginManager::class);
}
}
AddressFieldsetInputFilterFactory
class AddressFieldsetInputFilterFactory extends AbstractFieldsetInputFilterFactory
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
parent::setupRequirements($container, Address::class);
return new AddressFieldsetInputFilter($this->hydrator);
}
}
LocationFieldsetInputFilterFactory
class LocationFieldsetInputFilterFactory extends AbstractFieldsetInputFilterFactory
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
parent::setupRequirements($container, Location::class);
/** #var AddressFieldsetInputFilter $addressFieldsetInputFilter */
$addressFieldsetInputFilter = $this->inputFilterManager->get(AddressFieldsetInputFilter::class);
return new LocationFieldsetInputFilter(
$addressFieldsetInputFilter,
$this->hydrator
);
}
}
That takes care of the FieldsetInputFilterFactory classes. Just the Form left.
In my case I use the same abstract factory class as for the Fieldset classes.
LocationFormInputFilterFactory
class LocationFormInputFilterFactory extends AbstractFieldsetInputFilterFactory
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
parent::setupRequirements($container);
/** #var LocationFieldsetInputFilter $locationFieldsetInputFilter */
$locationFieldsetInputFilter = $this->getInputFilterManager()->get(LocationFieldsetInputFilter::class);
return new LocationFormInputFilter(
$locationFieldsetInputFilter,
$this->hydrator
);
}
}
So, that's all of the classes done. It's a complete setup. You might encounter some bugs as I modified my own code to remove getters/setters, code comments/hinting, error, property and variable checking without testing. But it should work ;)
However, we're nearly done. We still need:
config
usage in a Controller
print/use Form in a View
The config is easy:
'form_elements' => [
'factories' => [
AddressFieldset::class => AddressFieldsetFactory::class,
LocationFieldset::class => LocationFieldsetFactory::class,
LocationForm::class => LocationFormFactory::class,
],
],
'input_filters' => [
'factories' => [
AddressFieldsetInputFilter::class => AddressFieldsetInputFilterFactory::class,
LocationFieldsetInputFilter::class => LocationFieldsetInputFilterFactory::class,
LocationFormInputFilter::class => LocationFormInputFilterFactory::class,
],
],
That's it. That little bit ties all of the above classes together.
To get the Form into a Controller, you would do something like this in the Factory:
class EditControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$hydrator = new Reflection(); // or $container->get('hydrator') or $container->get(EntityManager::class), or whatever you use
/** #var FormElementManagerV3Polyfill $formElementManager */
$formElementManager = $container->get('FormElementManager');
/** #var LocationForm $form */
$form = $formElementManager->get(LocationForm::class); // See :) Easy, and re-usable
return new EditController($hydrator, $form);
}
}
A typical "Edit" action would be like this (mind, this one uses Doctrine's EntityManager as the hydrator):
public function editAction()
{
$id = $this->params()->fromRoute('id', null);
/** #var Location $entity */
$entity = $this->getObjectManager()->getRepository(Location::class)->find($id);
/** #var LocationForm $form */
$form = $this->form;
$form->bind($entity);
/** #var Request $request */
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
/** #var Location $object */
$object = $form->getObject();
$this->getObjectManager()->persist($object);
try {
$this->getObjectManager()->flush();
} catch (Exception $e) {
// exception handling
}
return $this->redirect()->toRoute('route/name', ['id' => $object->getId()]);
}
}
return [
'form' => $form,
'validationMessages' => $form->getMessages() ?: '',
];
}
And the View Partial would look like this (based on the return in the above action):
form($form) ?>
So, that's it. Fully fledged, re-usable classes. Single setup. And in the end just a single line in the Factory for the Controller.
Please take note though:
Form, Fieldset and InputFilter use "address" input name. Very important to keep these the same throughout as Zend does some magic based on the names to match Fieldset with InputFilter.
If you have any more questions about how this works, please read through the documentation in the repo's I linked first, before asking below this question. There's more there that should help you out more, for example for Collection handling.
I updated Doctrine 2.5 to 2.6 in my project and phpspec is broken.
The function getEntityChangeSet() is now returned by reference. It seems not to be supported by phpspec.
$unitOfWork
->getEntityChangeSet($site)
->willReturn(['_dataParent' => [0 => 2, 1 => 3]]);
The response is
returning by reference not supported
the underlying function (doctrine/doctrine2) is
public function & getEntityChangeSet($entity)
{
$oid = spl_object_hash($entity);
$data = [];
if (!isset($this->entityChangeSets[$oid])) {
return $data;
}
return $this->entityChangeSets[$oid];
}
Do you know if it's possible to bypass this or change test for make it work?
The answer has been given on Twitter by #Pamilme
You have to mock UnitOfWork with Mockery. An example can be found here:
/** #var UnitOfWork|MockInterface $unitOfWork */
$unitOfWork = Mockery::mock(UnitOfWork::class);
$unitOfWork->shouldReceive('getEntityChangeSet')->withArgs([$productAttribute->getWrappedObject()])->andReturn([
'configuration' => [
['choices' => [
'8ec40814-adef-4194-af91-5559b5f19236' => 'Banana',
'1739bc61-9e42-4c80-8b9a-f97f0579cccb' => 'Pineapple',
]],
['choices' => [
'8ec40814-adef-4194-af91-5559b5f19236' => 'Banana',
]],
],
]);
$entityManager->getUnitOfWork()->willReturn($unitOfWork);
if you extends TestCase on your test class, you can also do something like:
$uow = $this->createMock(UnitOfWork::class);
$uow->method('getEntityChangeSet')->willReturn(['_dataParent' => [0 => 2, 1 => 3]);
Unable to locate in the SilverStripe Documentation how to have a DataObject Model inject a collection of default records on /dev/build
Anybody able to point me in the right direction
This is what I currently have, and obviously I would like to inject pre-configured options into this aptly named Configuration model for my Module.
class Configuration extends DataObject
{
private static $db = array(
'Option' => 'Varchar',
'Value' => 'Varchar'
);
private static $summary_fields = array(
'Option' => 'Option',
'Value' => 'Value',
);
}
Thanks in advance for any direction/pointers.
UPDATE
I was turned onto SiteConfig by #Barry below
However in following his practice, requireDefaultRecords() is not injecting defaults
Note: I have since revisited /dev/build?flush
class RMSConfiguration extends DataExtension
{
private static $db = array(
'username' => 'Varchar',
'password' => 'Varchar',
'agent_id' => 'Varchar(15)',
'client_id' => 'Varchar(15)',
'testMode' => 'Int(1)',
'timezone' => 'Varchar',
'apiUrl' => 'Varchar(255)'
);
public function updateCMSFields(FieldList $fields)
{
$fields->addFieldsToTab(
"Root.RMSConfig",
array(
TextField::create('username', 'RMS Username'),
TextField::create('password', 'RMS Password'),
TextField::create('agent_id', 'RMS Agent ID'),
TextField::create('client_id', 'RMS Client ID'),
TextField::create('apiUrl', 'API Url'),
CheckboxField::create("testMode", 'Toggle Test Mode'),
DropdownField::create("timezone", 'Timezone', static::$timezones)
)
);
}
public function requireDefaultRecords()
{
parent::requireDefaultRecords();
$arrOptions = array(
'timezone' => 'Australia/Sydney',
'apiUrl' => 'https://api.example.com.au/',
'testMode' => 0
);
foreach ($arrOptions as $strOption => $strValue) {
if (!$configuration = self::get()->filter('Option', $strOption)->first()) {
$configuration = self::create(array( 'Option' => $strOption ));
}
$configuration->Value = $strValue;
$configuration->write();
}
}
/**
* List of timezones supported by PHP >=5.3.x
*
* #var array
*/
public static $timezones = array(
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
"Africa/Algiers",
...
...
"Zulu"
);
}
Using the function requireDefaultRecords in the DataObject - this is called during every dev/build.
Note: First check if the option exists to prevent duplicates as this will be called every time you dev build.
class Configuration extends DataObject {
private static $db = array(
'Option' => 'Varchar',
'Value' => 'Varchar'
);
private static $summary_fields = array(
'Option' => 'Option',
'Value' => 'Value',
);
function requireDefaultRecords() {
parent::requireDefaultRecords();
$arrOptions = array(
'Option1' => 'Value1',
'Option2' => 'Value2',
'Option3' => 'Value3',
);
foreach ($arrOptions as $strOption => $strValue) {
if (!$configuration = Configuration::get()->filter('Option',$strOption)->first())
$configuration = Configuration::create(array('Option' => $strOption));
$configuration->Value = $strValue;
$configuration->write();
}
}
}
One final comment is that there is a module for SiteConfig which is used by SilverStripe, most modules and where I would recommend you put configuration values like this instead.
If you do choose SiteConfig then please see the function populateDefaults and documentation for it's use, this is an example...
/**
* Sets the Date field to the current date.
*/
public function populateDefaults() {
$this->Date = date('Y-m-d');
parent::populateDefaults();
}
(if the above is used in an extensions it might need $this->owner->Date instead of $this->Date)
The above function isn't needed if all the values are static, instead it will read them just from this array (again within DataObject)
public static $defaults = array(
'Option1' => 'Value1',
'Option2' => 'Value2'
);
This works on any DataObject as well, but as SiteConfig manages one record and this populates that record once upon creation this is much more convenient for to use instead of requireDefaultRecords.
I seem to be having a problem with fixtures in Yii. The problem seems to be the following,
public $fixtures=array('projects'=>'Project');
The model Project exists and I have the fixtures in a file name tbl_project.php in the fixtures folder of tests and my table name is called tbl_project. Inside the fixtures file is the following.
return array(
'project1' => array(
'name' => 'Test Project 1',
'description' => 'This is test project 1',
'create_time' => '',
'create_user_id' => '',
'update_time' => '',
'update_user_id' => '',
),
'project2' => array(
'name' => 'Test Project 2',
'description' => 'This is test project 2',
'create_time' => '',
'create_user_id' => '',
'update_time' => '',
'update_user_id' => '',
),
'project3' => array(
'name' => 'Test Project 3',
'description' => 'This is test project 3',
'create_time' => '',
'create_user_id' => '',
'update_time' => '',
'update_user_id' => '',
),
);
This is actually from the book "Agile Web Application Development with Yii". When I run the test case I get the following with no test result information.
PHPUnit 3.6.10 by Sebastian Bergmann.
Configuration read from ETC/protected/tests/phpunit.xml
If I remove the fixtures array from the top I get the following.
Time: 0 seconds, Memory: 9.25Mb
There was 1 error:
1) ProjectTest::testRead
Exception: Unknown method 'projects' for class 'ProjectTest'.
Which obviously makes sense. I dont know what Im doing wrong.
Make sure the test class looks like this
class ProjectTest extends CDbTestCase{
protected $fixtures = array(
'projects' => 'Project',
);
public function testRead(){
$receivedProject = $this->projects('Project1');
$this->assertTrue($receivedProject instanceof Project);
$this->assertEquals($receivedProject->name,'test 1');
}
...`
Check the fixture configuration in protected/config/test.php ... Should look like ...
...
'components'=>array(
'fixture'=>array(
'class'=>'system.test.CDbFixtureManager',
),
'db'=>array(
'connectionString' =>'mysql:host=localhost;dbname=your_db_name',
'emulatePrepare' => true,
'username' => 'username',
'password' => 'passwd',
'charset' => 'utf8',
),
....
Ultimately check the permissions on the fixture file making sure it can be read
Also be sure you are calling parents setUp() method in your own setUp()
class SomeTest extends CDbTestCase {
public $fixtures = array(
'somes' => 'Some',
);
protected function setUp() {
parent::setUp();
// your code....
}
// your tests .......................
}
Is your test class deriven from CDbTestCase instead of CTestCase?
your Test Class should look something like this:
class ProjectTest extends CDbTestCase{
protected $fixtures = array(
'projects' => 'Project',
);
public function testRead(){
$receivedProject = $this->projects('Project1');
$this->assertTrue($receivedProject instanceof Project);
$this->assertEquals($receivedProject->name,'test 1');
}
class ProjectTest extends CDbTestCase
{
public function testCreate()
{
//CREATE a new Project
$newProject=new Project;
$newProjectName = 'Test Project Creation';
$newProject->setAttributes(array(
'name' => $newProjectName,
'description' => 'This is a test for new project creation',
'createTime' => '2009-09-09 00:00:00',
'createUser' => '1',
'updateTime' => '2009-09-09 00:00:00',
'updateUser' => '1',
)
);
$this->assertTrue($newProject->save(false));
//READ back the newly created Project to ensure the creation worked
$retrievedProject=Project::model()->findByPk($newProject->id);
$this->assertTrue($retrievedProject instanceof Project);
$this->assertEquals($newProjectName,$retrievedProject->name);
}
public function testRead()
{
$retrievedProject = $this->projects('project1');
$this->assertTrue($retrievedProject instanceof Project);
$this->assertEquals('Test Project 1',$retrievedProject->name);
}
public function testUpdate()
{
$project = $this->projects('project2');
$updatedProjectName = 'Updated Test Project 2';
$project->name = $updatedProjectName;
$this->assertTrue($project->save(false));
//read back the record again to ensure the update worked
$updatedProject=Project::model()->findByPk($project->id);
$this->assertTrue($updatedProject instanceof Project);
$this->assertEquals($updatedProjectName,$updatedProject->name);
}
public function testDelete()
{
$project = $this->projects('project2');
$savedProjectId = $project->id;
$this->assertTrue($project->delete());
$deletedProject=Project::model()->findByPk($savedProjectId);
$this->assertEquals(NULL,$deletedProject);
}
public function testGetUserOptions()
{
$project = $this->projects('project1');
$options = $project->userOptions;
$this->assertTrue(is_array($options));
$this->assertTrue(count($options) > 0);
}
public $fixtures=array(
'projects'=>'Project',
'users'=>'User',
'projUsrAssign'=>':tbl_project_user_assignment',
);
}
make sure that setup come with its parent
<i>public function setUp() {
parent::setUp();
}</i>