PHP MySQL Function Unit Testing - php

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.

Related

Testing - How this common scenario is classified and handled

I’m just diving into testing/test driven development in php, and am a bit confused about how to handle a very typical scenario in our legacy codebase. Our codebase does not have many classes, it’s very functional programming oriented.
In this example code, which is pseudocode, I want to write a test for getAndOrderUsersByAge(). As you can see it first gets all the users from our database (in a giant array lets say, because we don’t have objects/classes) then sorts them. This is of course a VERY common occurrence in our code, manipulating or analyzing data gotten from the database; not objects, but an array of database rows basically.
Note that I've added an error in the if statement to simulate an error I want my tests to catch.
//gets all users in our db into an array, then sorts them by age
function getAndOrderUsersByAge(){
//get all users from the db
$all_users = getAllUsers();
//sort
$all_users_sorted = sortUsers($all_users)
//notice I've made a mistake here on purpose, left the "d" off of "$all_users_sorted"
//so the function will always return false, a mistake...which I want tests to catch
if(!empty($all_users_sorte){
return $all_users_sorted;
}
else{
return false;
}
}
My questions are:
1.) Is testing the function getAndOrderUsersByAge() unit testing, integration testing, or …?
2.) In my (unit?) test(s) of getAndOrderUsersByAge(), I want to assume that getAllUsers (the function which gets things from the db) behaves correctly because I only want to test getAndOrderUsersByAge() right? I’m not really sure what “concept” this falls under when I’m reading the two books I have on testing. Is this where I would “mock”, e.g. create some fake data for getAllUsers() to return?
3.) What tests would you write for getAndOrderUsersByAge()? I think I would write one that ensures a non-empty array is returned when getAllUsers() returns a non-empty array, as this would catch the error I've created. I'm not sure what else though.
Any tips, recommended reading material, etc. is very welcomed.
1.) Is testing the function getAndOrderUsersByAge() unit testing, integration testing, or …?
Any test that you do at code level is unit testing, basically testing methods , classes, interfaces. Tests that validate a unit or block of code.
2.) In my (unit?) test(s) of getAndOrderUsersByAge(), I want to assume that getAllUsers (the function which gets things from the db)
You can test getAndOrderUsersByAge() by actual data or mock data. Mock data is preferred so you will have more control on the test and you can create more test cases for sortUsers function.
3.) What tests would you write for getAndOrderUsersByAge()?
The test should be of nature that would validate your sortUsers function. I am not sure how your data looks but some tests could be:
1) data set that will have valid data , which will pass the test
2) data set that has invalid data like special characters in name
3) data set that has junk in age field or floating numbers or out of range integers, basically test how your function behaves when age has different type of data
4) data set that has null values
5) data set with 1 element array
6) data set with repeated element array
7) data set with invalid types in array

Escape SQL queries in PHP PostgreSQL

I have a website with lots of PHP files (really a lot...), which use the pg_query and pg_exec functions which do not
escape the apostrophe in Postgre SQL queries.
However, for security reasons and the ability to store names with
apostrophe in my database I want to add an escaping mechanism for my database input. A possible solution is to go
through every PHP file and change the pg_query and pg_exec to use pg_query_params but it is both time consuming
and error prone. A good idea would be to somehow override the pg_query and pg_exec to wrapper functions that would
do the escaping without having to change any PHP file but in this case I guess I will have to change PHP function
definitions and recompile it which is not very ideal.
So, the question is open and any ideas that would
allow to do what I want with minimum time consumption are very welcome.
You post no code but I guess you have this:
$name = "O'Brian";
$result = pg_query($conn, "SELECT id FROM customer WHERE name='{$name}'");
... and you'd need to have this:
$name = "O'Brian";
$result = pg_query_params($conn, 'SELECT id FROM customer WHERE name=$1', array($name));
... but you think the task will consume an unreasonable amount of time.
While it's certainly complex, what alternatives do you have? You cannot override pg_query() but it'd be extremely simple to search and replace it for my_pg_query(). And now what? Your custom function will just see strings:
SELECT id FROM customer WHERE name='O'Brian'
SELECT id FROM customer WHERE name='foo' OR '1'='1'
Even if you manage to implement a bug-free SQL parser:
It won't work reliably with invalid SQL.
It won't be able to determine whether the query is the product of intentional SQL injection.
Just take it easy and fix queries one by one. It'll take time but possibly not as much as you think. Your app will be increasingly better as you progress.
This is a perfect example of when a database layer and associated API will save you loads of time. A good solution would be to make a DB class as a singleton, which you can instantiate from anywhere in your app. A simple set of wrapper functions will allow you to make all queries to the DB go through one point, so you can then alter the way they work very easily. You can also change from one DB to another, or from one DB vendor to another without touching the rest of the app.
The problem you are having with escaping is properly solved by using the PDO interface, instead of functions like pg_query(), which makes escaping unnecessary. Seeing as you'll have to alter everywhere in your app that uses the DB, you may as well refactor to use this pattern at the same time as it'll be the same amount of work.
class db_wrapper {
// Singleton stuff
private $instance;
private function __construct() {
// Connect to DB and store connection somewhere
}
public static function get_db() {
if (isset($instance)) {
return $instance;
}
return $instance = new db_wrapper();
}
// Public API
public function query($sql, array $vars) {
// Use PDO to connect to database and execute query
}
}
// Other parts of your app look like this:
function do_something() {
$db = db_wrapper::get_db();
$sql = "SELECT * FROM table1 WHERE column = :name";
$params = array('name' => 'valuename');
$result = $db->query($sql, $params);
// Use $result for something.
}

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

Using the Data Mapper Pattern, Should the Entities (Domain Objects) know about the Mapper?

I'm working with Doctrine2 for the first time, but I think this question is generic enough to not be dependent on a specific ORM.
Should the entities in a Data Mapper pattern be aware - and use - the Mapper?
I have a few specific examples, but they all seem to boil down to the same general question.
If I'm dealing with data from an external source - for example a User has many Messages - and the external source simply provides the latest few entities (like an RSS feed), how can $user->addMessage($message) check for duplicates unless it either is aware of the Mapper, or it 'searches' through the collection (seems like an inefficient thing to do).
Of course a Controller or Transaction Script could check for duplicates before adding the message to the user - but that doesn't seem quite right, and would lead to code duplication.
If I have a large collection - again a User with many Messages - how can the User entity provide limiting and pagination for the collection without actually proxying a Mapper call?
Again, the Controller or Transaction Script or whatever is using the Entity could use the Mapper directly to retrieve a collection of the User's Messages limited by count, date range, or other factors - but that too would lead to code duplication.
Is the answer using Repositories and making the Entity aware of them? (At least for Doctrine2, and whatever analogous concept is used by other ORMs.) At that point the Entity is still relatively decoupled from the Mapper.
Rule #1: Keep your domain model simple and straightforward.
First, don't prematurely optimize something because you think it may be inefficient. Build your domain so that the objects and syntax flow correctly. Keep the interfaces clean: $user->addMessage($message) is clean, precise and unambiguous. Underneath the hood you can utilize any number of patterns/techniques to ensure that integrity is maintained (caching, lookups, etc). You can utilize Services to orchestrate (complex) object dependencies, probably overkill for this but here is a basic sample/idea.
class User
{
public function addMessage(Message $message)
{
// One solution, loop through all messages first, throw error if already exists
$this->messages[] $message;
}
public function getMessage()
{
return $this->messages;
}
}
class MessageService
{
public function addUserMessage(User $user, Message $message)
{
// Ensure unique message for user
// One solution is loop through $user->getMessages() here and make sure unique
// This is more or less the only path to adding a message, so ensure its integrity here before proceeding
// There could also be ACL checks placed here as well
// You could also create functions that provide checks to determine whether certain criteria are met/unmet before proceeding
if ($this->doesUserHaveMessage($user,$message)) {
throw Exception...
}
$user->addMessage($message);
}
// Note, this may not be the correct place for this function to "live"
public function doesUserHaveMessage(User $user, Message $message)
{
// Do a database lookup here
return ($user->hasMessage($message) ? true
}
}
class MessageRepository
{
public function find(/* criteria */)
{
// Use caching here
return $message;
}
}
class MessageFactory
{
public function createMessage($data)
{
//
$message = new Message();
// setters
return $message;
}
}
// Application code
$user = $userRepository->find(/* lookup criteria */);
$message = $messageFactory->create(/* data */);
// Could wrap in try/catch
$messageService->sendUserMessage($user,$message);
Been working with Doctrine2 as well. Your domain entity objects are just that objects...they should not have any idea of where they came from, the domain model just manages them and passes them around to the various functions that manage and manipulate them.
Looking back over, I'm not sure that I completely answered your question. However, I don't think that the entities themselves should have any access to the mappers. Create Services/Repositories/Whatever to operate on the objects and utilize the appropriate techniques in those functions...
Don't overengineer it from the onset either. Keep your domain focused on its goal and refactor when performance is actually an issue.
IMO, an Entity should be oblivious of where it came from, who created it and how to populate its related Entities. In the ORM I use (my own) I am able to define joins between two tables and limiting its results by specifying (in C#) :
SearchCriteria sc = new SearchCriteria();
sc.AddSort("Message.CREATED_DATE","DESC");
sc.MaxRows = 10;
results = Mapper.Read(sc, new User(new Message());
That will result in a join which is limited to 10 items, ordered by date create of message. The Message items will be added to each User. If I write:
results = Mapper.Read(sc, new Message(new User());
the join is reversed.
So, it is possible to make Entities completely unaware of the mapper.
No.
Here's why: trust. You cannot trust data to act on the benefit of the system. You can only trust the system to act on data. This is a fundamental of programming logic.
Let's say something nasty slipped into the data and it was intended for XSS. If a data chunk is performing actions or if it's evaluated, then the XSS code gets blended into things and it will open a security hole.
Let not the left hand know what the right hand doeth! (mostly because you don't want to know)

Code coverage with phpunit; can't get to one place

In the xdebug code coverage, it shows the line "return false;" (below "!$r") as not covered by my tests. But, the $sql is basically hard-coded. How do I get coverage on that? Do I overwrite "$table" somehow? Or kill the database server for this part of the test?
I guess this is probably telling me I'm not writing my model very well, right? Because I can't test it well. How can I write this better?
Since this one line is not covered, the whole method is not covered and the reports are off.
I'm fairly new to phpunit. Thanks.
public function retrieve_all()
{
$table = $this->tablename();
$sql = "SELECT t.* FROM `{$table}` as t";
$r = dbq ( $sql, 'read' );
if(!$r)
{
return false;
}
$ret = array ();
while ( $rs = mysql_fetch_array ( $r, MYSQL_ASSOC ) )
{
$ret[] = $rs;
}
return $ret;
}
In theory, you should be better separating the model and all the database related code.
In exemple, in Zend Framework, the quickstart guide advise you to have :
your model classes
your data mappers, whose role is to "translate" the model into the database model.
your DAOs (or table data gateway) that do the direct access to the tables
This is a really interesting model, you should have a look at it, it enables you to really separate the model from the data, and thus to perform tests only onto the model part (and don't care about any database problem/question)
But in your code, I suggest you to perform a test where you have the dbq() function to return false (maybe having the db connexion to be impossible "on purpose"), in order to have full code coverage.
I often have these kind of situations, where testing all the "error cases" takes you too much time, so I give up having 100% code coverage.
I guess the function dbq() performs a database query. Just unplug your database connection and re-run the test.
The reason you have trouble testing is that you are using a global resource: your database connection. You would normally avoid this problem by providing your connection object to the test method (or class).

Categories