How to test the interaction with the database PHPUnit? - php

To test the PHP code I am using PHPUnit.
Presently engaged in writing of the application architecture, while using the template Data Mapper. Accordingly, most of the tests should be reduced to the verification code interaction with the database. CRUD operations with an entity, etc. I think there should be checked directly on the database, and not replaced by a stub objects. Is testing the interaction with the database module? And how to conduct this test. Whether to use DBUnit. how? I ask not that you can read in the documentation, and specifically about the best solutions that come with experience. Thank you!
protected function update($obj) {
$values = array(":id" => $obj->getId(),
":title" => $obj->getTitle(),
":path" => $obj->getPath(),
":type" => $obj->getType(),
":size" => $obj->getSize());
$this->updateStmt->execute($values);
}

Related

Yii Unit-Testing with a Logged in Web User

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

Sandboxing Mongo->execute(MongoCode) in PHP to prevent any DB interaction

As a feature in the software I'm writing, I'm allowing myself to create calculators written in JS to compute the fees to be applied to a specific set of data, using said data as a reference. Since I'm using Mongo, I can run this safely server-side, and the browser can just call a php page and get the response. The function will be written from an administration control panel and saved to the database. I of course won't be doing any db interactions from inside that function, but executing mongocode is done within the database, so mongocode by nature can do db.foo
Just to protect myself and anyone else who might end up writing calculators, I've set db = null; in $context being passed to new MongoCode()
It looks a bit like this:
$sample = [
'estimatedvalue' => 200,
'estimatedcost' => 400,
'contractor' => false,
'db' => null,
];
$fees = [
'_id' => new MongoId(),
'name' => 'Friendly name!',
'code' => new MongoCode('function(){return (contractor ? estimatedCost : estimatedValue)*0.01; /* If a contractor is doing the work, base fee on cost. */}', $sample),
];
$a = $this->siteDB->execute($fees['code']);
if(isset($a['errno'])){
echo $a['errmsg'];
}else{
var_dump($a['retval']);
}
Fortunately, that works, and if I was to inject that into all context, there would be no db. commands runnable. I don't want to create a point where NoSQL injection can happen!
An example of something that this prevents is:
'code' => new MongoCode('function(){db.foo.remove();}', $sample),
// Since db is now null, the above won't work
My concern: Are there any other variables that exist in this MongoCode Execute environment that could be potentially harmful to leave in a user-editable function? I couldn't find any documentation on what else is accessible through mongocode functions. If db is it, then I'm all set!
This is not safe, and I don't think you can have a user-editable JS function that is. For example, this requires no variables and shuts down your server:
> db.eval("(new Mongo('localhost:27017')).getDB('admin').shutdownServer()")
They can insert data, drop databases, connect to other servers in your system, and generally wreck havoc.
If you are trying to allow a user-editable compute function in JavaScript, use a separate JS engine, pull the values from MongoDB, and pass the values + user-defined function to the totally separate JS engine.

How will one make an unit test in PHP if an ORM layer is used?

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

Phpunit testing with database

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();
}

PHP/JS Form Class - Array vs OO

I have recently begun working on a PHP/JS Form Class that will also include a SQL Form builder (eg. building simple forms from sql and auto inserts/updates).
I have tried several classes (zend_form, clonefish, PHP Form Builder Class, phorms etc) but as yet haven't come across a complete solution that is simple, customizable and complete (both server side and client side validation, covers all simple html elements and lots of dhtml elements: sorting, wysiwyg, mutli file upload, date picker, ajax validation etc)
My question is why do some "classes" implement elements via an array and others via proper OO class calls.
eg.
Clonefish (popular commercial php class):
$config = Array(
'username' => Array(
'type' => 'inputText',
'displayname' => 'Username',
validation => Array(
Array(
'type' => 'string',
'minimum' => 5,
'maximum' => 15,
),
),
));
$clonefish = new clonefish( 'loginform', 'test.php', 'POST' );
$clonefish->addElements( $config, $_POST );
Then others eg. Zend_Form
$form = new Zend_Form;
$username = new Zend_Form_Element_Text('username');
$username->addValidator(new Zend_Validate_Alnum());
$form->addElement($username);
I realise Zend_Form can pass elements in via an array similar to clonefish but why do this?
Is there any benefit? It seems to make things more complicated especially when using a proper IDE like Komodo.
Any thoughts would be appreciated as I dont want to get too far down the track and realize there was great benefit in using arrays to add elements (although this wouldn't be much of a task to add on).
Cheers
My question is why do some "classes" implement elements via an array and others via proper OO class calls.
For convenience. It's less verbose and it feels less like coding and more like configuration and you need less intimate knowledge of the API.
Btw, the reason you have not yet come across a complete solution that is simple, customizable and complete is because it is not simple. Forms, their validation and rendering is complex, especially if you want to have it customizable for any purpose. ZF's form components are a good example of how to properly decouple and separate all concerns to get the ultimate extensible form builder (including client side code through Zend_Dojo or ZendX_Jquery). But they are also a great example of the complexity required for this. Even with the convenient array configuration, it is damn difficult to make them bend to your will, especially if you need to depart from the default configuration and rendering.
Why to use objects? Becouase they are a much more complex types. Consider the following example (I never useed Zend_Form so I don't even know its architecture):
class MySuperAlnumValidator extends Zend_Validate_Alnum {
protected $forbiddenWords = array();
public function addForbiddenWord($word) {
$this->forbiddenWords[] = $word;
}
// Override Zend_Value_Alnum::validate() - I don't know whether such a method even exists
// but you know what's the point
public function validate() {
parent::validate();
if (in_array($this->value, $this->forbiddenWords) {
throw new Exception('Invalid value.');
}
return $this->value;
}
}
// -----------------------
$validator = new MySuperAlnumValidator();
$validator->addForbiddenWord('admin');
$validator->addForbiddenWord('administrator');
$username->addValidator($validator);
This is only a simple example but when you start writing more complex validators/form fields/etc. then objects are, in principle, the only meaningful tool.

Categories