I am trying to focus a bit on unit testing using PHPunit.
I have found a very good tutorial over here http://blog.nickbelhomme.com/php/phpunit-training-course-for-free_282
But there is something I'm missing and don't yet understand how to do.
I have a user module which maintains all information about users. And there is a function save which saves the user in the database. So I have a testFunction
public function testCanCreateUser()
{
$userData = array(
'userName' => 'User1',
'firstName' => 'Joey',
'lastName' => 'Hendricks',
'email' => 'Joey#hendricks.com',
'password' => 'f$tfe8F'
);
$user = new Model_User($userData);
$user->save();
}
The first time when I will run my test this will work. Since the database is empty. But When I run my tests for the second time it won't work since my system doesn't allow the same user twice in the db. So In order to do this I have to recreate my testdatabase every time before I run my tests. What is the best way to do this?
Or is this problem to be solved on a different way?
If you want to test your business logic: Mock away the Database class and return fake data
If you want to test the class that fires the SQL statements (and imho you could test that too since I kinda wanna know if my code works fine with a real db in the backend) it gets a little complicated but there are ways to do it:
Using setUp() and tearDown() to get a consistent state for your data before running your tests is (imho) a fine way to write db-driven unittests. It can get annoying to write lots of custom sql by hand though.
To make your life a little easier you can look into the DbUnit extension and see if that works for your Application.
If you really want to dive into Unittesting database interactions the best read on the subject is (imho) the chapter on db-unittesting in Sebastian Bergmanns phpqa book.
Could your application allow for a custom database name and automated setup of all tables it may also be possible to set the db up once with a lot of testdata and use that data in all your tests. You could be carefull so though that one test doesn't rely on data written by another one.
Run tests with other copy of the database that is empty and/or cleared in setUp() or tearDown() methods, but be careful not to do what github did.
If you're using a good database (i.e. not MySQL with MyISAM tables) you can wrap test in a transaction and roll it back after the test:
function setUp() { $this->db->exec("BEGIN"); }
function tearDown() { $this->db->exec("ROLLBACK"); }
The downside is that you can't test code that uses transactions (unless you abstract that and emulate with savepoints, but that's iffy).
Ideally you should use dependency injection and run tests on fake database class:
$fakedb = new DatabaseThatDoesntReallySaveThings();
$user = new Model_User($fakedb, $userData);
$user->save();
$this->assertTrue($fakedb->wasAskedToSaveUser());
I think you can use tearDown() method to clean your saved data.
protected $_user;
public function testCanCreateUser()
{
...
$this->_user = new Model_User($userData);
$this->_user->save();
}
public function tearDown()
{
$this->_user->delete();
}
Related
I'm using laravel but it's not important, when you create a controller with laravel command line tool, it puts 4 default function in there for create and update.
create and store for save
edit and update for well update!
This is what laravel suggest for Shop controller.
class ShopController extends Controller
{
public function create()
{
// return create view
}
public function store(Request $request)
{
// save a shop
}
public function edit($id)
{
// find a shop , return edit view
}
public function update(Request $request, $id)
{
// find the shop with id , update the shop
}
}
But I like to use the same methods for showing view and store/update my row and avoid writing lots of extra code.
class ShopController extends Controller
{
public function create($id = 0)
{
return view('shop-create' , ['edit'=> Shop::find($id)]);
}
public function store(Request $request , $id = 0 )
{
$whitelist = [
'title'=>'required',
'phone'=>'present|numeric' ,
'address'=>'present' ,
];
$this->validate($request, $whitelist );
$shop = Shop::findOrNew($id) ;
// find a shop with given id or create a new shop instance
foreach($whitelist as $k=>$v)
$shop->$k = $request[$k];
$shop->save();
}
}
Naturally I go with what I like (second option), but since laravel suggest the first way, just out of curiosity is there any reason why I shouldn't do it like this? Is this considered bad practice in any way?
Nothing wrong, but you code will be harder to understand, IMHO.
e.g.:
What does this method do? It's called create, but it also edits?
The view is called shop-create but it also edits?
Passing a 0 parameter as default for id and trying to find it every time is unnecessary.
public function create($id = 0)
{
return view('shop-create' , ['edit'=> Shop::find($id)]);
}
Although you're thinking that you are simplifying your code, you are turning it more complicated since you are breaking the Single Responsibility principle from SOLID.
It's easier to understand if you have something like the Laravel suggestion.
Also you keep a very common pattern that any Laravel developer will understand, so you can hire someone to take care of your code and do not worry if he will understand.
There is nothing wrong with doing it your way. The "laravel" way you mention is when you create a Restful resource controller and is simply one way to tackle it.
I guess those controller methods were picked because they line up nicely to a "restful" type of controller. If you were to be building a true rest api, then how you do it becomes far more strict from a standards point of view (not imposed by laravel, but line up better to the laravel way).
If you aren't creating a public facing api, or something that is going to be consumed by external entities, then I say design your controllers that work best for you and your team
This is how i usually do it, this way you can still have different validation by using the requests and it's still clear (imo) what the functions do.
public function store(AdminPartnerRequest $request)
{
return $this->handleCreateOrUpdate($request);
}
public function update(AdminPartnerRequest $request, $id)
{
return $this->handleCreateOrUpdate($request,true, $id);
}
private function handleCreateOrUpdate($request, $edit = false, $id = null)
{
if ($edit){
$partner = Partner::find($id);
} else{
$partner = new Partner();
}
$partner->name = $request->input('name');
$partner->picture = $request->input('image');
$partner->save();
return \Redirect::route('admin.partners.index');
}
Using same function for save() and update() is good idea but at the same time it will increase complexity .. One point is If in future you want to change anything you need to change it only at one place.
But at the same time you need to take some extra care.
As your function should be more dynamic.
1) Multiple records manipulation : you may require to update more than one raws at the same time so your function should be enough flexible to insert/update single/multiple values by the same function. Meaning , single query should be fired for multiple records in both the cases.
2) Validation if value already exist : When you are going to check some validation ...
in insert case you only need to check if the value is exist in db or not
when in update case you need to check with exclusion of current id
e.g.
for insert case
$this->validate($request, [
'email' => 'required|string|email|unique:tablename,email'
]);
for update case
$this->validate($request, [
'email' => 'required|string|email|unique:tablename,email,'.$id.',id'
]);
And at last very small point but need to be considered ..
3) Success message : At the time of insertion message should be "added successfully" and at updation time Record "updated successfully"
Small project, do whatever you want. Large with other developers, follow the conventions.
Coding conventions are a set of guidelines for a specific programming language that recommend programming style, practices, and methods for each aspect of a program written in that language. These conventions usually cover file organization, indentation, comments, declarations, statements, white space, naming conventions, programming practices, programming principles, programming rules of thumb, architectural best practices, etc. These are guidelines for software structural quality. Software programmers are highly recommended to follow these guidelines to help improve the readability of their source code and make software maintenance easier. Coding conventions are only applicable to the human maintainers and peer reviewers of a software project. Conventions may be formalized in a documented set of rules that an entire team or company follows, or may be as informal as the habitual coding practices of an individual. Coding conventions are not enforced by compilers. -- https://en.wikipedia.org/wiki/Coding_conventions
I used this method in a last project of mine, we called the store() and update() function manage() instead and had a getManage() which would use the same view for creating and editing. I liked this method a lot yet came across a few things worth noting. Sadly the cons outway the pros if you ever have to face those issues :(
Pros:
Smaller code - No longer do you have duplicate lines of code in your store() and update() function.
Faster to re-use with basic models - ctrl+c ctrl+v ctrl+f ctrl+r if you know what I mean.
Easier to add/change input values - An extra value would not mean having to change store() and update() to make sure they both utilize the extra input.
One function to rule them all - As long as you are not doing anything special, you can even define one function for everything. Need to change something? You've got one function, no worries.
Cons:
Code is harder to understand for others (or an older you) - If someone is new to this method or hasn't used it in a while, understanding what happens within your function is a little harder than having two separate ones.
Validation is a nuisance - As stated in this answer validation may be different for create and update. Meaning you may sometimes have to write two validations which will eventually lead to messy code and we don't want that!
Value insertion wasn't as cool as I thought - If you want to use the same predefined array to create or update then you may run into the problem of wanting to insert values on create yet never want to update them. This eventually led to either ugly if statements or two predefined arrays.
Eventually it's up to what you're going to make and what you want to do. If you have a basic website which will manage blog posts and pages then have no worries going for a shared store() and update() function. Yet if you're creating a huge CMS with many models, relations and different create and update input values (which may mean different validation) then I'd go with what Laravel recommends. It would be much easier to maintain in the long run and you won't have to deal with headaches, hacky fixes and unclean code.
Whatever you do, don't do both in different controllers! That would be confusing.
By the way, if you're wondering what kind of project I had - it was a huge CMS. So even though it was very useful and easy in some cases, it was sadly not worth it.
Nothing wrong, but in that case you have to maintain proper comments that specify that your function perform add / edit and for that you are using some variable like $id or some thing else. If it is available than you can update the record otherwise insert it.
I'm working on an application written in PHP. I decided to follow the MVC architecture.
However, as the code gets bigger and bigger, I realized that some code gets duplicated in some cases. Also, I'm still confused whether I should use static functions when quering the database or not.
Let's take an example on how I do it :
class User {
private id;
private name;
private age;
}
Now, inside this class I will write methods that operate on a single user instance (CRUD operations). On the other hand, I added general static functions to deal with multiple users like :
public static function getUsers()
The main problem that I'm facing is that I have to access fields through the results when I need to loop through users in my views. for example :
$users = User::getUsers();
// View
foreach($users as $user) {
echo $user['firstname'];
echo $user['lastname'];
}
I decided to do this because I didn't feel it's necessary to create a single user instance for all the users just to do some simple data processing like displaying their informations. But, what if I change the table fields names ? I have to go through all the code and change those fields, and this is what bothers me.
So my question is, how do you deal with database queries like that, and is it fine to use static functions when querying the database. And finally, where is it logical to store those "displaying" functions like the one I talked about ?
Your approach seems fine, howerver I would still use caching like memcached to cache values and then you can remove static.
public function getUsers() {
$users = $cacheObj->get('all_users');
if ($users === false) {
//use your query to grab users and set it to cache
$users = "FROM QUERY";
$cacheObj->set('all_users', $users);
}
return $users;
}
(M)odel (V)iew (C)ontroller is a great choice choice, but my advice is look at using a framework. The con is they can have a step learning curve, pro is it does a lot of heavy lifting. But if you want to proceed on your own fair play, it can be tough to do it yourself.
Location wise you have a choice because the model is not clearly define:
You'll hear the term "business logic" used, basically Model has everything baring views and the controllers. The controllers should be lean only moving data then returning it to the view.
You model houses DB interaction, data conversions, timezone changes, general day to day functions.
Moudle
/User
/Model
/DB or (Entities and Mapper)
/Utils
I use Zend and it uses table gateways for standard CRUD to avoid repetition.
Where you have the getUsers() method you just pass a array to it, and it becomes really reusable and you'd just have different arrays in various controller actions and it builds the queries for you from the array info.
Example:
$data = array ('id' => 26)
$userMapper->getUsers($data);
to get user 26
enter code here
$data = array ('active' => 1, 'name' => 'Darren')
$userMapper->getUsers($data);`
to get active users named Darren
I hope this help.
Im writing some unit tests and bear with me I am still very new to unit testing.
The issue I am having is a lot of my saves invoke a behaviour that requires
the users id from Yii::app()->user->id.
However when I run the UnitTest I get problems as the user isn't logged in.
Is there anyway I can either ignore the behaviour by a flag (e.g. if ($isInTestingMode)) or log the user in within the testing class?
I would probably build a user object that you use in your tests. And then in the appropriate tests (as part of the setup method, like ernie describes in his comment), swap in the testing user object.
The test user object would then have a method that works like this:
public function getId() {
return 12;
}
public function getIsGuest() {
return false;
}
The above is what they call a 'Fake' object.
In your setup method you'd use the following lines:
Yii::app()->configure(array(
'components' => array(
'user' => array(
'class' => 'path.to.FakeUser',
)
)
));
You can also add that to your test config file if you want that to be the default user (and then swap in the normal CWebUser/WebUser model in tests that need to have a non-logged in user.
Or you could have a flag you set for your FakeUser (isLoggedIn = true/false) in each unit test. I'd probably go with this option myself ...
Tests like these are not unit tests. Unit tests are for testing individual functions in a class. A "logged in user" covers a large swath of code (logging in, session management, navigating to a specific page, etc.). I believe you're looking for functional testing. You can learn more about that here: http://www.yiiframework.com/doc/guide/1.1/en/test.functional
I need to test a number of functions that I have created using PHP 5 which carry out the required database CRUD type actions (SELECT, UPDATE, INSERT, DELETE) which are required by my web application.
I have been looking at PHP unit testing suites such as Simple Test and PHP Unit which seem to offer what I need however I am unsure how I am meant to achieve this, as equivalence partitioning and boundary analysis isn't all that clear. Do I just need to input different variables and vary this? This seems rather pointless as a string that is different may not necessarily make any difference.
Any guidance on this would be helpful as I have not encountered this before.
If you are testing the interaction between your PHP code and your MySQL database, you are performing integration testing, rather than unit testing.
Here are some examples of integration testing with Enhance PHP framework, it tests a repository class that saves and retrieves a Tenant object.
Instead of running against a pre-populated database, it runs on an entirely empty database and creates and destroys the tables as it goes using a simple table helper. This removes the dependency on particular data being in the right state in a test database, which is hard to keep in step.
<?php
class TenantRepositoryTestFixture extends EnhanceTestFixture {
private $Target;
public function SetUp() {
$tables = new TableHelper();
$tables->CreateTenantTable();
$this->Target = Enhance::GetCodeCoverageWrapper('TenantRepository');
}
public function TearDown() {
$tables = new TableHelper();
$tables->DropTenantTable();
}
public function SaveWithNewTenantExpectSavedTest() {
$tenant = new Tenant();
$tenant->Name = 'test';
$saved = $this->Target->Save($tenant);
$result = $this->Target->GetById($saved->Id);
Assert::AreNotIdentical(0, $result->Id);
Assert::AreIdentical($tenant->Name, $result->Name);
}
public function SaveWithExistingTenantExpectSavedTest() {
$tenant = new Tenant();
$tenant->Name = 'test';
$saved = $this->Target->Save($tenant);
$saved->Name = 'changed';
$saved = $this->Target->Save($saved);
$result = $this->Target->GetById($saved->Id);
Assert::AreIdentical($saved->Id, $result->Id);
Assert::AreIdentical($saved->Name, $result->Name);
}
}
?>
Generally the idea with unit testing is to ensure that, if you make a change, you can simply run a simple series of tests to ensure no existing functionality will break. So that said, you'll want to cover the typical data you're expecting, as well as edge/boundary cases, which might include strings with quotes (to verify that they're being escaped properly), SQL injection attacks (same), empty strings, strings of different encoding, NULL, a boolean true, etc. Each test should verify that, given the data you input, you're getting the expected result, in this case (respectively): escaped string inserted, escaped string inserted, empty string inserted, different encoding converted or thrown out then inserted, an error thrown on a NULL value, the string 'true' inserted, etc.
I haven't used either test framework in a few years, but I remember having good results with PHPUnit.
I have a class let's say Person. The ORM layer has generated based on the sql structure the corresponding objects. The class person has a method: Get($id). In the Get method, the object Person is called and the field from the table are retrieved.
I basically want to make the following unit test: create a new person and check if the Get method is returning the right information.
How is the unit testing supposed to work in this condition ?
Do I need to create a separate database ( just the structure ), and make the creation/selection from that database?
Should the boostrap file load the same configuration as the framework I'm using but change the configuration file so It works with the fake database ?
Should I clean the new database each after each test ?
I was also wandering after seeing your responses if simulating an ORM response without actually building a new database is not the way to go ?
How is the unit testing supposed to work in this condition ?
Generally, you should split your unittests mentally in two parts:
one part of your code is testable without the database, so you can stub or mock the methods that do the database access
the other part of your tests needs to work with the database, since it tests if the ORM is used correctly.
Do I need to create a separate database
This depends on your needs. Rails apps generally have testing/development/production "environments" - database, configuration, storage directories.
Testing is for running unit tests, dev for developing things and production for running the live server. While developing, you run against the dev configuration and thus your development database. For unit tests, the testing env is used which has the benefit that i.e. users in the database are not deleted or broken.
I like that concept; in my phpunit tests I often do have a switch in the bootstrap that changes which configuration file is loaded. Just remember that your development database often contains more data than a single unit test needs, and you probably hand-crafted that data and do not want to lose. Also, another database does not cost money.
Should I clean the new database each after each test?
I mostly clean the tables only that will be used in the test. Cleaning your database makes sure you don't get side-effects from previous tests.
Check out Phactory. I prefer it over the database extensions included in PHPUnit and it makes it really easy to insert records into your test db.
require_once 'Phactory/lib/Phactory.php';
Phactory::setConnection(new PDO('sqlite:test.db'));
Phactory::define('user', array('name' => 'Test User',
'email' => 'user#example.com'));
$user = Phactory::create('user'); // creates a row in the 'users' table
print("Hello, {$user->name}!"); // prints "Hello, Test User!"
Your System Under Test (SUT) will need to connect to your test database. The idea is that you populate just the records you need for the method you are testing. The orm layer shouldn't matter if the test db has all the same tables and fields as your production database.
PHPUnit also provides some help with this, have a look at Database Testing.
Essentially you can write you Test classes so that they extend PHPUnit_Extensions_Database_TestCase and then use the getConnection() and getDataSet() functions to load up data for the test.
require_once 'PHPUnit/Extensions/Database/TestCase.php';
class PersonTest extends PHPUnit_Extensions_Database_TestCase
{
protected function getConnection() {
$pdo = new PDO('mysql:host=localhost;dbname=application_test', 'root', '');
return $this->createDefaultDBConnection($pdo, 'application_test');
}
protected function getDataSet() {
return $this->createMySQLXMLDataSet('person.xml');
}
Then you can define exactly what you want to test in the database in the XML.
You can also assert that the resulting DataSet from your tests is equal to what you expect with:
public function testCreate() {
// Execute some code with your ORM to create a person.
$actual = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
$actual->addTable('person');
$expected = $this->createMySQLXMLDataSet('person_create_expected.xml');
$this->assertDataSetsEqual($expected, $actual);
}
In this example, we are only comparing the resulting person table... So person_create_expected.xml should only contain the person table as well.
To create the XML's you can use mysqldump.
mysqldump --xml -t -u root -p application_test > person.xml