Unit Testing a FuelPHP Model - php

So I have a very simple (at least right now it is) model that returns a contact via it's primary key ID:
class Model_Contact extends \Fuel\Core\Model
{
public function get_by_id($contact_id)
{
return Entity_Contact::find_by_pk($contact_id);
}
}
The Entity_Contact class looks like this (irrelevant array content omitted):
class Entity_Contact extends \Core\Entity_Base
{
protected static $_table_name = 'contacts';
protected static $_properties = array(...);
protected static $_public_settable_properties = array(...);
protected static $_rules = array(...);
}
Note: \Core\Entity_Base extends \Fuel\Core\Model_Crud
I might use this in a controller like so:
$model = new Model_Contact();
$contact = $model->get_by_id(4);
I know that in order to unit test this, I should mock out the actual database call (Entity_Contact::find_by_pk), but I'm not sure how to do this. Since I'm using Fuel's Model_crud functionality (where the DB accessors are actually a part of the domain object model), I'm not sure that I can completely mock the database---or maybe I'm missing something.
So the question: how would you write a test for Model_Contact::get_by_id()?
Thanks in advance!

You can mock it easily if you use AspectMock.
https://github.com/Codeception/AspectMock

Your test may have to create the objects so you can test your methods.
Create the objects that get stored to the database.
Test get_by_id
Delete your created object
It's not the most glorious thing in the world, but it would accomplish testing your method... meh?

Related

Override returned values from Model

Is it possible to override values from Model->fetchAll() so it work globally. I have tried to override this in model, but does not work:
class Application_Model_DbTable_OdbcPush extends Zend_Db_Table_Abstract
{
public function __get(string $col)
{
$res = parent::__get($col);
if ($col == "lastrun") {
$res = ($res == "1912-12-12 00:00:00+07" ? NULL : $res);
}
return $res;
}
//...
}
In a controller:
$odbcModel = new Application_Model_DbTable_OdbcPush();
$rs = $odbcModel->fetchAll( $select );
I want to override value returned from fetchAll(), find() etc when col name is "lastrun";
The way you're going about this isn't going to work. __get is used to get data from protected or private properties and typically used in conjunction with getters.
For example, if you implemented __get() in your Application_Model_DbTable_OdbcPush class you could do something like:
$model = new Application_Model_DbTable_OdbcPush();
//echo out the _primary property (primary key of the table)
echo $model->primary;
and expect it to work. Because _primary exists as a property in Zend_Db_Table_Abstract.
To do what you want to do you'll need to do it after the result set has been returned (unless you want to rewrite the whole Zend Db component). Just run the result set through a foreach and change the value of lastrun to whatever you want.
I tried to find a place to override the Zend Db components to do what you want, but it would involve to many classes.
Remember that when using DbTable classes, they only interact with one table. You'll need to duplicate code for every table you want to effect or you'll need to extend a base class of some kind.
You always have the option to use straight Sql to frame whatever query you can come up with.
Good Luck!
Found the answer, for community i share here :D
http://framework.zend.com/manual/1.12/en/zend.db.table.row.html
So we have to overload Zend_Db_Table_Row and assign it to model/dbtable:
class Application_Model_DbTable_Row_OdbcPush extends Zend_Db_Table_Row_Abstract
{
// do some override here
}
class Application_Model_DbTable_OdbcPush extends Zend_Db_Table_Abstract
{
protected $_name = 'odbcpush';
protected $_primary = 'id';
private $_global = null;
protected $_rowClass = "Application_Model_DbTable_Row_OdbcPush";
// etc
}

How to get current object in component (symfony 1.4)?

The only code i found is:
class modulenameComponents extends sfComponents
{
public function executeAction(sfWebRequest $request)
{
$object = $this->getContext()->getController()
->getAction($this->getModuleName(), $request->getParameter('action'))
->getRoute()->getObject();
}
}
But it doesn't work if component includes in different module.
The component is called from the template, so pass your object from the template:
<?php include_component('modulename', 'actionName', array('myObject' => $myObject)) ?>
You will be able to access your object in your component via $this:
class modulenameComponents extends sfComponents
{
public function executeActionName()
{
// your object
$object = $this->myObject;
...
}
}
Regarding Symfony 1.4.x doctrine admin generator where everything is done in generator.yml, components receive the form object, which contains the model object. So, to get a hold of the current model object, do the following:
$myModelObject = $this->form->getObject();
NOTE: This is the model object, not the table object. To get the table object, do this:
$myModelTableObject = $this->form->getObject()->getTable();
I had to figure this out myself, so hope it helps someone...
There may be a better way, but using sfConfig::set() in your action an sfConfig::get() in your component should do the trick.

About actions in symfony

In my website I have a section of groups. The groups have events, forum, etc. I use symfony and I have the following files:
class GroupActions extends sfActions{
public function preExecute(){
$request = $this->context->getRequest();
$this->group = fGroup::getGroupById($request->getRequestParameter('id_group'));
//Group security
$this->group_security = $this->group->getSecurity();
//More required actions about groups
....
}
class ForumActions extends GroupActions {
public function preExecute() {
parent::preEcecute();
$this->forum = $this->group->getForums();
}
ForumActions extends GroupActions because I need group data. For example, the request:
/groups/2/forum
forwards to ForumActions and I can get group's data.
Is this correct?
Thanks.
Here there, i think there is a mistake here where it says:
$this->group = fGroup::getGroupById($request->getRequestParameter('id_group'));
And it should be:
$this->group = fGroup::getGroupById($request->getParameter('id_group'));
Maybe that is way you ended up with an empty group info. Also, its always a got practice to define your class variables in the action and then initialize them in some method (also getters and setters could help.) so you assure that variables could be reached be others that might need them.

Is this the correct way to do this PHP class?

Hello I am just learning more about using classes in PHP. I know the code below is crap but I need help.
Can someone just let me know if I am going in the right direction.
My goal is to have this class included into a user profile page, when a new profile object is created, I would like for it to retrieve all the profile data from mysql, then I would like to be able to display any item on the page by just using something like this
$profile = New Profile;
echo $profile->user_name;
Here is my code so far, please tell me what is wrong so far or if I am going in the right direction?
Also instead of using echo $profile->user_name; for the 50+ profile mysql fileds, sometimes I need to do stuff with the data, for example the join date and birthdate have other code that must run to convert them, also if a record is empty then I would like to show an alternative value, so with that knowlege, should I be using methods? Like 50+ different methods?
<?PHP
//Profile.class.php file
class Profile
{
//set some profile variables
public $userid;
public $pic_url;
public $location_lat;
public $location_long;
public $user_name;
public $f_name;
public $l_name;
public $country;
public $usa_state;
public $other_state;
public $zip_code;
public $city;
public $gender;
public $birth_date;
public $date_create;
public $date_last_visit;
public $user_role;
public $photo_url;
public $user_status;
public $friend_count;
public $comment_count;
public $forum_post_count;
public $referral_count;
public $referral_count_total;
public $setting_public_profile;
public $setting_online;
public $profile_purpose;
public $profile_height;
public $profile_body_type;
public $profile_ethnicity;
public $profile_occupation;
public $profile_marital_status;
public $profile_sex_orientation;
public $profile_home_town;
public $profile_religion;
public $profile_smoker;
public $profile_drinker;
public $profile_kids;
public $profile_education;
public $profile_income;
public $profile_headline;
public $profile_about_me;
public $profile_like_to_meet;
public $profile_interest;
public $profile_music;
public $profile_television;
public $profile_books;
public $profile_heroes;
public $profile_here_for;
public $profile_counter;
function __construct($session)
{
// coming soon
}
//get profile data
function getProfile_info(){
$sql = "SELECT user_name,f_name,l_name,country,usa_state,other_state,zip_code,city,gender,birth_date,date_created,date_last_visit,
user_role,photo_url,user_status,friend_count,comment_count,forum_post_count,referral_count,referral_count_total,
setting_public_profile,setting_online,profile_purpose,profile_height,profile_body_type,profile_ethnicity,
profile_occupation,profile_marital_status,profile_sex_orientation,profile_home_town,profile_religion,
profile_smoker,profile_drinker,profile_kids,profile_education,profile_income,profile_headline,profile_about_me,
profile_like_to_meet,profile_interest,profile_music,profile_television,profile_books,profile_heroes,profile_here_for,profile_counter
FROM users WHERE user_id=$profileid AND user_role > 0";
$result_profile = Database::executequery($sql);
if ($profile = mysql_fetch_assoc($result_profile)) {
//result is found so set some variables
$this->user_name = $profile['user_name'];
$this->f_name = $profile['f_name'];
$this->l_name = $profile['l_name'];
$this->country = $profile['country'];
$this->usa_state = $profile['usa_state'];
$this->other_state = $profile['other_state'];
$this->zip_code = $profile['zip_code'];
$this->city = $profile['city'];
$this->gender = $profile['gender'];
$this->birth_date = $profile['birth_date'];
$this->date_created = $profile['date_created'];
$this->date_last_visit = $profile['date_last_visit'];
$this->user_role = $profile['user_role'];
$this->photo_url = $profile['photo_url'];
$this->user_status = $profile['user_status'];
$this->friend_count = $profile['friend_count'];
$this->comment_count = $profile['comment_count'];
$this->forum_post_count = $profile['forum_post_count'];
$this->referral_count = $profile['referral_count'];
$this->referral_count_total = $profile['referral_count_total'];
$this->setting_public_profile = $profile['setting_public_profile'];
$this->setting_online = $profile['setting_online'];
$this->profile_purpose = $profile['profile_purpose'];
$this->profile_height = $profile['profile_height'];
$this->profile_body_type = $profile['profile_body_type'];
$this->profile_ethnicity = $profile['profile_ethnicity'];
$this->profile_occupation = $profile['profile_occupation'];
$this->profile_marital_status = $profile['profile_marital_status'];
$this->profile_sex_orientation = $profile['profile_sex_orientation'];
$this->profile_home_town = $profile['profile_home_town'];
$this->profile_religion = $profile['profile_religion'];
$this->profile_smoker = $profile['profile_smoker'];
$this->profile_drinker = $profile['profile_drinker'];
$this->profile_kids = $profile['profile_kids'];
$this->profile_education = $profile['profile_education'];
$this->profile_income = $profile['profile_income'];
$this->profile_headline = $profile['profile_headline'];
$this->profile_about_me = $profile['profile_about_me'];
$this->profile_like_to_meet = $profile['profile_like_to_meet'];
$this->profile_interest = $profile['profile_interest'];
$this->profile_music = $profile['profile_music'];
$this->profile_television = $profile['profile_television'];
$this->profile_books = $profile['profile_books'];
$this->profile_heroes = $profile['profile_heroes'];
$this->profile_here_for = $profile['profile_here_for'];
$this->profile_counter = $profile['profile_counter'];
}
//this part is not done either...........
return $this->pic_url;
}
}
You might want to take a look at PHP's magic methods which allow you to create a small number of methods (typically "get" and "set" methods), which you can then use to return/set a large number of private/protected variables very easily. You could then have eg the following code (abstract but hopefully you'll get the idea):
class Profile
{
private $_profile;
// $_profile is set somewhere else, as per your original code
public function __get($name)
{
if (array_key_exists($name, $this->_profile)) {
return $this->_profile[$name];
}
}
public function __set($name, $value)
{
// you would normally do some sanity checking here too
// to make sure you're not just setting random variables
$this->_profile[$name] = $value;
}
}
As others have suggested as well, maybe looking into something like an ORM or similar (Doctrine, ActiveRecord etc) might be a worthwhile exercise, where all the above is done for you :-)
Edit: I should probably have mentioned how you'd access the properties after you implement the above (for completeness!)
$profile = new Profile;
// setting
$profile->user_name = "JoeBloggs";
// retrieving
echo $profile->user_name;
and these will use the magic methods defined above.
You should look into making some kind of class to abstract this all, so that your "Profile" could extend it, and all that functionality you've written would already be in place.
You might be interested in a readymade solution - these are called object relational mappers.
You should check out PHP ActiveRecord, which should easily allow you to do something like this without writing ORM code yourself.
Other similar libraries include Doctrine and Outlet.
Don't use a whole bunch of public variables. At worst, make it one variable, such as $profile. Then all the fields are $profile['body_type'] or whatever.
This looks like a Data Class to me, which Martin Fowler calls a code smell in his book Refactoring.
Data classes are like children. They are okay as a starting point, but to participate as a grownup object, they need to take some responsibility.
He points out that, as is the case here,
In the early stages these classes may have public fields. If so, you should immediately Encapsulate Field before anyone notices.
If you turn your many fields into one or several several associative arrays, then Fowler's advice is
check to see whether they are properly encapsulated and apply Encapsulate Collection if they aren't. Use Remove Setting Method on any field that should not be changed.
Later on, when you have your Profile class has been endowed with behaviors, and other classes (its clients) use those behaviors, it may make sense to move some of those behaviors (and any associated data) to the client classes using Move Method.
If you can't move a whole method, use Extract Method to create a method that can be moved. After a while, you can start using Hide Method on the getters and setters.
Normally, a class would be created to abstract the things you can do to an object (messages you can send). The way you created it is more like a dictionary: a one-to-one mapping of the PHP syntaxis to the database fields. There's not much added value in that: you insert one extra layer of indirection without having a clear benefit.
Rather, the class would have to contain what's called a 'state', containing e.g. an id field of a certain table, and some methods (some...) to e.g. "addressString()", "marriedTo()", ....
If you worry about performance, you can cache the fields of the table, which is a totally different concern and should be implemented by another class that can be aggregated (a Cache class or whatsoever).
The main OO violation I see in this design is the violation of the "Tell, don't ask" principle.

Testing an abstract method of a child-class from an abstract class

To stay with the same example I used here:
I now want to test the implementation of the protected methods in my child-classes.
Because I stub them in my test of the abstract class, the implementations themselves aren't tested.
But a protected-method isn't tested normally, so that's why I'd like your suggestions on how to test them after all.
Just like my other thread I'd like to solve this without refactoring my code.
Parent-class:
abstract class Order
{
public function __construct( $orderId, User $user )
{
$this->id = $this->findOrderId( $user->getId(), $orderId );
if ($this->id !== false) {
$this->setOrderData();
}
}
abstract protected function findOrderId( $userId, $orderIdToSearch );
private function setOrderData()
{
...
}
}
Child-class to test:
public class OrderTypeA extends Order
{
protected function findOrderId($userId, $orderId)
{
...
}
}
Test code:
class OrderTypeATest extends PHPUnit_Framework_TestCase
{
public function testFindOrderId() {
???
}
}
You can test protected/private methods using the reflection. Read this tutorial. There you will find, among the other solutions, the direct one:
/**
* Call protected/private method of a class.
*
* #param object &$object Instantiated object that we will run method on.
* #param string $methodName Method name to call
* #param array $parameters Array of parameters to pass into method.
*
* #return mixed Method return.
*/
public function invokeMethod(&$object, $methodName, array $parameters = array())
{
$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
Also, regarding the previous question of yours, where you are trying to test abstract class. The solution with phpunit mocking must work. But if you use PHP 7, you can use Anonymous classes to achieve the same result:
abstract class Order
{
protected $id;
public function __construct($orderId, $userId)
{
$this->id = $this->findOrderId($userId, $orderId);
if ($this->id !== false) {
$this->setOrderData();
}
}
abstract protected function findOrderId($userId, $orderIdToSearch);
private function setOrderData()
{
echo 'setOrderData';
}
}
$orderId = 1;
$userId = 1;
$order = new class($orderId, $userId) extends Order {
protected function findOrderId($userId, $orderIdToSearch)
{
return 1;
}
};
You will end up with the working $order object, which is ready for testing. Also it is good idea to put this code in the setUp() method of the Test Case.
If you are only get a valid $this->id when a order is found right. Do some like:
$order = new OrderTypeA($orderId, $user);
$this->assertNotEquals(false,$order->id);
Or if $orderId equals $this->id
$order = new OrderTypeA($orderId, $user);
$this->assertEquals($orderId,$order->id);
But not enough code/logic shown here to tell you more;)
Your abstraction does not make sense to me.
I understand that you have an object representing an order. You instantiate it by giving a user and an order id. However there's more than one type of order, and the difference between these types of orders is in the way you search them in the database storage? That doesn't sound right.
Your code does tell a weird story. You have this order id, and the first thing you do is search the order id. I just thought that you already HAVE the order id, so there shouldn't be a need to yet again search for it. Or maybe that method has the wrong name, and instead of findOrderId() it should be called findOrderById() - or findUserOrderById().
Also, you do work in the constructor. Searching for stuff should not be done there.
Your testing problem comes from the fact that you decided to implement different search strategies as an abstract method. You have to test a protected abstract method, which is not really easy. It makes it also hard to property test the main abstract order class because you have to provide an implementation - and this implementation sounds like it conceals a database access layer, so there can be plenty of things going wrong in the real code.
I suggest not allowing the order to search itself. Searching for orders should be done outside of the order object. That way, you'll likely implement that search as a public method, which can be normally tested. The code searching for orders will then decide whether you have a successfully found OrderTypeA, or maybe a missing MissingOrderTypeA, both extending the order class. The order objects should carry the order data, not the search logic to find them in the database.
Hint: If you have problems testing your code, it is 99,9% likely that your code is trying to do things the wrong way. This is not saying that things cannot be done that way, it is saying that you are about to produce hard to test code, that is also hard to maintain, and that it is a good idea to look for alternative strategies to implement the solution. Elegant code is always easy to test, because all the necessary methods are public in the relevant classes, and therefore can work together as intended.

Categories