PHP Developers Network

A community of PHP developers offering assistance, advice, discussion, and friendship.
 
Loading
It is currently Wed Oct 17, 2018 6:46 pm

All times are UTC - 5 hours




Post new topic Reply to topic  [ 14 posts ] 
Author Message
PostPosted: Wed Feb 17, 2010 2:24 pm 
Offline
Forum Newbie

Joined: Fri Jul 17, 2009 12:57 pm
Posts: 8
I have a global variable that is a database, and it's inside most of my classes, and the functions use that variable to extract or insert data. The functions do not return anything. Unfortunately I'm using mysql, and PHPUnit encourages us to use sqlite3, so I can't create a table by memory.

I feel like PHPUnit's database extension isn't fleshed out yet, considering their tutorial on Operations and Database Best Practices are blank.

Is there a proper way to test functions in classes using mysql queries? Should I be using mock objects instead? It seems like I need to have a test table and just use that to test these functions that use queries. If the functions do not return a value, is it possible to unit test it?

Thank you.


Top
 Profile  
 
PostPosted: Wed Feb 17, 2010 8:29 pm 
Offline
DevNet Master

Joined: Wed Feb 11, 2004 4:23 pm
Posts: 4872
Location: Palm beach, Florida
I agree with you, its' database extension is of little value.

Including Queries in the harness
- I just create a startTransaction() and rollbock() calls in my setup & tear down methods. (transaction rollback)
- Another way to do it is truncate() on teardown (setup/tear down clean up)

Isolate the business logic from the query
Another way is to subclass your class and test that subclass. Move the queries into protected methods that all start with the word "find", "delete", etc.. and group them together at the bottom of the class. Null out those methods on the test specific extension.

Isolating the business logic from the querying is a highly desirable trait of a system. It means your system can be re-used in more scenarios. But on the other hand I think including your queries in the harness itself is a huge benefit, however the two methods are not mutually exclusive.


Top
 Profile  
 
PostPosted: Thu Feb 18, 2010 10:09 am 
Offline
DevNet Master
User avatar

Joined: Mon Sep 19, 2005 6:24 am
Posts: 3587
Location: London
I'll refrain from posting my thoughts on this topic again :P


Top
 Profile  
 
PostPosted: Thu Feb 18, 2010 3:41 pm 
Offline
DevNet Master

Joined: Wed Feb 11, 2004 4:23 pm
Posts: 4872
Location: Palm beach, Florida
What Jenk does is assert on the SQL itself. That is probably going to yield tests that fit the definition of "unit test" better, however the down side that I can see is it makes it easier to have a test be wrong (and therefore give me a false sense of security).

Also for every rule of thumb theres a counter rule. If your black box behavior does not change, but the querying logic/underlying schema changes, then asserting on the SQL is a bad idea. If your black box behavior changes but the underlying schema stays the same asserting on the SQL is the best idea.

There's no right or wrong paths to go down.


Top
 Profile  
 
PostPosted: Fri Feb 19, 2010 12:22 pm 
Offline
DevNet Master

Joined: Thu Jan 30, 2003 9:26 pm
Posts: 2893
Location: Glasgow, Scotland
Actually I'd argue that it is essential to test with a real database. Mocked connection objects cast a shadow which can hide all kinds of unpleasantness in the data access layer. Data access code must be tested too - perhaps more so than anything else. Bad application logic can always be reworked but bad, damaged data is bad forever.


Top
 Profile  
 
PostPosted: Mon Feb 22, 2010 2:01 am 
Offline
Forum Newbie

Joined: Fri Jul 17, 2009 12:57 pm
Posts: 8
Thanks for your input everyone.

josh wrote:
I agree with you, its' database extension is of little value.

Including Queries in the harness
- I just create a startTransaction() and rollbock() calls in my setup & tear down methods. (transaction rollback)
- Another way to do it is truncate() on teardown (setup/tear down clean up)

Isolate the business logic from the query
Another way is to subclass your class and test that subclass. Move the queries into protected methods that all start with the word "find", "delete", etc.. and group them together at the bottom of the class. Null out those methods on the test specific extension.

Isolating the business logic from the querying is a highly desirable trait of a system. It means your system can be re-used in more scenarios. But on the other hand I think including your queries in the harness itself is a huge benefit, however the two methods are not mutually exclusive.


Hi Josh,

Just to make sure I understand your approach, let's say we have a class:


Syntax: [ Download ] [ Hide ]
 
class MyClass
{
    public $myVar;
 
    public function MyClass($arg1, $arg2)
    {
        global $database;
 
        $query = $database->query('SELECT'.$arg1.'FROM'.$arg2);
 
        $rows = mysqli_num_rows($query);
    }
}
 


You want me to convert it to something like this?

Syntax: [ Download ] [ Hide ]
 
class MySubClass extends MyClass
{
    public $myVar;
 
    public function MyClass($arg1, $arg2)
    {
        global $database;
 
        $query = 'SELECT'.$arg1.'FROM'.$arg2;
 
        $rows = mysqli_num_rows($query);
    }
 
    protected function queryFunction($arg1, $arg2)
    {
        return 'SELECT'.$arg1.'FROM'.$arg2;
    }
}
 


That way I can test the query by testing queryFunction(). Is that the right idea?

Also is there no way for PHPUnit to check variables within a function, or even in classes like $myVar? Can I test $rows without having function MyClass return anything?

Thank you.


Top
 Profile  
 
PostPosted: Mon Feb 22, 2010 9:48 am 
Offline
DevNet Master
User avatar

Joined: Mon Sep 19, 2005 6:24 am
Posts: 3587
Location: London
McGruff wrote:
Actually I'd argue that it is essential to test with a real database. Mocked connection objects cast a shadow which can hide all kinds of unpleasantness in the data access layer. Data access code must be tested too - perhaps more so than anything else. Bad application logic can always be reworked but bad, damaged data is bad forever.
What unpleasantness do you speak of? If a connection mock receives an expected query, that you the developer, knows is correct (semantically, and objectively) what shadow does this cast?

Your last point is moot and somewhat sensational. You can get bad/damaged data at anytime. Tests will not prevent this, and won't even help prevent it. :)

100% test coverage is a myth and frankly undesirable. If your code is covered, happy days. If you start trying to cover other people's products or code, you've taken it too far. If you start testing for every type of edge case, you've taken it way too far.


Last edited by Jenk on Mon Feb 22, 2010 10:00 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Mon Feb 22, 2010 9:51 am 
Offline
DevNet Master
User avatar

Joined: Fri Jan 18, 2008 1:36 am
Posts: 3549
Location: Israel, ME
I think he referred to loss / damage to data with a malformed query. Tests should and could prevent (some of) it.


Top
 Profile  
 
PostPosted: Mon Feb 22, 2010 10:04 am 
Offline
DevNet Master
User avatar

Joined: Mon Sep 19, 2005 6:24 am
Posts: 3587
Location: London
But you'd still lose that data. Just running the test for the first time will do that, if you are connected to a database. Of course, it'll be on a test/sandbox database and noticed long before it touches live.

I'm not advocating you don't functionally test, but functional test should be done by hand at least once before deployment. I.e. you try and login, you try and add something to something else, you try and delete something, etc. Automated tests are there to prove your concept, and to perform quick and frequent checks as you progress. They are not a replacement for actual, physical testing.

It's just occurred to me that it is somewhat ironic that testing against a string of characters (SQL query) is being noted as a bad thing to do by some (all but me?) and yet.. testing against a string of characters (code, asserts, etc, also written by the developer) is being heavily relied upon by those same people... both are doing the same thing.


Top
 Profile  
 
PostPosted: Mon Feb 22, 2010 10:09 am 
Offline
DevNet Master
User avatar

Joined: Fri Jan 18, 2008 1:36 am
Posts: 3549
Location: Israel, ME
Quote:
But you'd still lose that data. Just running the test will do that. Of course, it'll be on a test/sandbox database and noticed long before it touches live.

The test isn't testing that you lose the data, it's testing for validity of the operations. Obviously, if the test fails, there's a problem that needs fixing. And yes, there's a big difference between losing test data and production data.

Quote:
It's just occurred to me that it is somewhat ironic that testing against a string of characters (SQL query) is being noted as a bad thing to do by some (all but me?) and yet.. testing against a string of characters (code, asserts, etc, also written by the developer) is being heavily relied upon by those same people... both are doing the same thing.


Testing application logic and doing string comparison is not the same thing. The logic that produces dynamic queries can not be reproduced by static strings. String comparison = brittle tests, for the most part


Top
 Profile  
 
PostPosted: Mon Feb 22, 2010 12:01 pm 
Offline
DevNet Master

Joined: Wed Feb 11, 2004 4:23 pm
Posts: 4872
Location: Palm beach, Florida
Testing the queries does not mean you are re-testing mysql.You are in fact testing the SQL, which is part of your code. You are not just testing that the query runs and returns some data, you are testing that that data works with the code.

I write tests to facilitate change.

"Axiom 1" If theres a bug in the test and the test passes, you've also got a bug in production (almost always).
"Axiom 2" If the test is simply doing a string assertion then the probability of a test bug and production regression occurring together is 100%, when developers want to change something they simply change the SQL in both places.
"Axiom 3" If the tests don't provide any benefit that simply running the query manually before & after changing it would provide, then there is no reason to write said test.

I think this is sound logic. Jenk your database testing strategy is basically to run the query and eyeball the results. If thats what works for you then great, but I think functional tests can & should test the database sometimes. What you are doing, asserting on the SQL string, provides no benefit other then a reminder for you to do the manual testing. Why not just tie a ribbon around your finger, it would be more cost effective :D

I've heard you argue not to use "private". The same argument applies here I think. You're just disabling yourself from changing your production code as easily. You now have to make edits in two places, and worst of all developers just start editing the tests every time a change is needed (making the test pointless because eventually it wouldn't be testing anything but the presence of bugs)


Top
 Profile  
 
PostPosted: Tue Feb 23, 2010 6:39 am 
Offline
DevNet Master
User avatar

Joined: Mon Sep 19, 2005 6:24 am
Posts: 3587
Location: London
Not sure what you meant by your last point, you're supposed to edit your tests when changes are needed. Refactoring your tests to suit new requirements is the first point of call :)

I can see your points regarding the rest. :)


Top
 Profile  
 
PostPosted: Tue Feb 23, 2010 4:00 pm 
Offline
DevNet Master

Joined: Thu Jan 30, 2003 9:26 pm
Posts: 2893
Location: Glasgow, Scotland
Jenk wrote:
What unpleasantness do you speak of? If a connection mock receives an expected query, that you the developer, knows is correct (semantically, and objectively) what shadow does this cast?

But you don't know it's correct unless you test it. There are a finite number of options.

(1) "I know mysql!" You've uploaded a database server Matrix-style directly into your brain. You just need to glance at a query to see if it's correct. Not a bad solution but even if the sql is all OK you don't know if it will interact correctly with the real schema. [double meaning] This is mock testing. [/double meaning]

In the real world it's not actually possible to work like this. I don't why anyone recommends it. You cannot write and compare mock expectations except for trivially simple queries. You just can't do it.

(2) Test manually. Error prone and time-intensive. No use at all if you're as lazy as I am.

(3) Test it on the site users. This is the most common form of testing. You don't half get some flak though when a customer order goes missing. In fact, that makes it even more hassle than manual testing so forget it.

(4) Automated tests which hit a real database. Easy for the terminally Lazy? Check. Not too sensitive? Check. No false positives? Check. Verifies the sql works with the target schema? Check. Rock solid, fully-verified data manipulation code? Check.


Top
 Profile  
 
PostPosted: Tue Feb 23, 2010 6:24 pm 
Offline
DevNet Master

Joined: Wed Feb 11, 2004 4:23 pm
Posts: 4872
Location: Palm beach, Florida
Jenk wrote:
Not sure what you meant by your last point, you're supposed to edit your tests when changes are needed.

BUT, that act of editing the test should have some point or benefit. Here it provides none if you simply assert on the SQL itself. The programmer is simply inconvenienced (changes it in both places).. introduces a bug, and goes on about his day. All the test did was slow down development (and possibly remind the developer to conduct manual testing).

That's why I like the term TDD over BDD. Even though all my assertion methods use the word "should" (borrowed from BDD), I like that calling it a "test" reminds me it should actually verify something. Otherwise its all superfluous feel good code.

For a simple SELECT query, if doing "test last", it would sometimes (read rarely though) make sense to assert on the query itself.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group