Out of the box, CakePHP 1.x has a default and test datasource. When running unit tests it uses the test database and before each test it can fixturise the data from code, or import data from your default database.
In an app I'm building I have a couple of cases where it would be useful to specify in some of the model fixtures a database other than test:
the datasource is a web service and the data returned can't easily be replicated with the test database,
there are large read-only tables and it is very slow (and unnecessary) to recreate all this data before each test.
I've looked at CakeTestFixture but I can't see a good way to do this yet. Perhaps each model really wants to have a useDbConfig and useTestConfig property?
I can hack my way round this in some cases by doing something like:
function startTest() {
$this->Flock =& ClassRegistry::init('Flock');
$this->Flock->Sheep->useDbConfig = 'readonly_db';
$this->Flock->Dog->useDbConfig = 'soap';
}
But this will only cover specific dependencies for one test case, and doing this across a suite of tests would be a maintenance nightmare.
Has anyone come across a similar situation and did you have a solution?
create the $test db connection in database.php and cake will take care of the rest. Look at some of the examples in the core test cases and you will see there is nothing special added.
Related
I'm trying to understand the unit tests when a database is involved. My classes heavily depends on a database to do their work. I already had a look at many many answers here on stackoverflow and on the internet in general, but I'm not really happy with what I found.
One of the common problems that I see when a database is involved (MySQL in my case) is that the tests are really really long to execute and one possible solution is to use SQLite.
I can't use it everywhere in the tests because some classes uses RAW queries instead of the Doctrine querybuilder to bypass the limitation of unknown datetime functions. SQLite has a different set of functions for date values and this means that to use it in the tests I should rewrite my queries twice, one for MySQL and one for SQLite.
I decided then to stick with MySQL, but creating and dropping the schema every time a test runs takes so much time. I know that the SchemaTool class can handle the creation of a subset of the schema: would be a good solution to create only the tables I really need in the test suite or should I always use the full schema?
Example of the part of code (pseudo-code) I'm trying to test
NotificationManagerClass
constructor(EntityManager)
loadNotifications()
deleteNotification()
updateNotification()
as you can see, I inject the entity manager in the constructor of the class. The class is a service registered in the Symfony container. In the controller I then get this service and use its methods. Inside the methods I use the querybuilder because I must have access to some other services and a repository isn't container-aware. How can I decouple more than this my class? I can't think a way to do it
You mix a lot of words together, which don't all have the meaning you gave them. A quick summary of the words:
Unit tests - Tests only one class. It doesn't test nor instantiate any other classes (it uses mocks or stubs for them). It shouldn't be using a database, it should mock doctrine instead.
Web tests - These tests test how multiple classes work together using database. These are often quite slow, so you don't want to have very many very specific tests here.
MySQL and SQLite are both just database driver. SQLite is as slow as MySQL and it really doesn't matter which one you use (well, it does matter, you have to use the same driver as you use in production)
You can't mock everything, because you decided to use raw mysql functions (bad choice)... Always avoid to use mysql queries inside classes. Use either Doctrine (there are a lot of simple ways to bypass the unknown datetime functions) or put the raw queries in the Repository and mock the repository.
In short:
Don't use anything other than the tested object in unit tests (mock/stub everything else)
Web Tests should be using production tools
Don't use mysql directly in a class
Test involving a database is not a unit test. Your repositories should be tested with integration or functional tests (or acceptance if you write such).
If you have many tests involving database, that probably means you took a wrong turn somewhere, either with:
code design - your classes are coupled to the database
deciding how to test - writing too many integration tests instead of unit tests
Might be worth looking into the test pyramid: http://martinfowler.com/bliki/TestPyramid.html
By looking into how to speed up your tests you'll only continue walking the wrong path. The solution is to write more unit tests and less integration tests. Unit tests are fast, independent and isolated, therefore harder to break. Integration tests are more fragile and slow. Look into FIRST properties of unit tests.
Btw: SQLite is a dead end. It doesn't behave like mysql as it doesn't support constraints.
Your example class could be modelled as:
class NotificationManager
{
public function __construct(MyNotificationRepository $repository, MyFabulousService $service)
{}
// ... other methods
}
This way you can inject fakes of both repository and your service and test the NotificationManager in isolation.
There's no much value in unit testing query builders as you'd be testing doctrine instead of your code. It is also hard, since the query class is declared final. You can test if queries return correct results functionally. There's gonna be lot less functional tests needed if you unit test your classes properly.
If you made the MyNotificationRepository an interface, you could even decouple from doctrine.
I have read in another document that classes which have methods with connections to a database have to be tested by integration tests. I tried to find an example, but I didn't find any, only theory. I've very little experience with testing. Can you give me an example? a link?, something (instead of only theory, because I read a lot), with something like this?. I think this is not an strange case. If its possible with php and sql or similar.
If you dont know about integration tests, how do you test this kinds of problems?
class database{
public $server;
public $database;
public $password;
public $user;
public $descriptor;
function __construct(){
$this->server="localhost";
$this->database="mydb";
$this->user="stackoverflow";
$this->password="1234";
$this->descriptor=mysql_connect($this->server,$this->user,$this->password);
mysql_select_db($this->database,$this->descriptor);
}
function make_request($query){
$response=mysql_query($query,$this->descriptor);
return $response;
}
function close_connection(){
mysql_close($this->descriptor);
}
function number_rows($result_query){
return mysql_num_rows($result_query);
}
}
Look into PHPUnit for unit testing. You can then use mock objects to simulate the dependencies (DB calls) to return dummy data. Because you're trying to test some unit of logic and not that your database layer, it should be acceptable and give you a reasonable amount of confidence that your code is working.
Another option is to use integration tests, but this requires that you put a lot of effort into the setup and teardown of your tests. You need to make sure any data required for your tests exists within your database. It's common to use transactions for these types of tests which you can then rollback after your finished running those tests.
Which code is it you're trying to test? Is it the code that uses your database class? If so, you don't need to introduce PDO, as your database class already provides an abstraction (well, it would be much safer if public $descriptor was private). So, you should start by mocking your database class and then testing the code that uses it. PHPUnit has a sophisticated mocking system.
Another thing you might want to be testing is this database class itself? In that case you'll have to accept the MySQL database dependency, and the speed hit that gives you. Set up a completely dedicated MySQL database for that testing. If you don't mind the speed hit, consider creating the database in a setUp() function and removing it after each test in a tearDown() function.
The third thing you might want to be testing is that your website (or whatever) works as a whole, and connects to the database, etc. This is functional testing (also called integration testing). If your system offers a web services API, then you could use that to test it. Otherwise you could use Selenium (still using PHPUnit) to test by remote control of a real browser. If your tests will only read from the database, you could use your live system; but it would be much more sensible to assume a write will happen at some point, so test against a test version of the website, with its own dedicated test database.
The advantage of functional testing is it high level, testing for all interactions between components, testing the system as the end user will use it. The downside is when a test fails it only tells you something is wrong, not exactly which component is to blame. So we have unit tests to test each component in isolation.
I know you this is more text, less code, than you wanted! But you have actually asked quite a theoretical question! Just try setting up some unit tests of this code, watch what problems occur over the first month of using them, and you'll understand why and where to use mocks.
I am trying to implement some unit tests into a legacy PHP application.
There have been a number of challenges with this, but in particular for this question, I am currently looking at a small class that manages the app config.
The class interface is pretty simple; it does the following:
The constructor calls a Populate method, that uses our Recordset class to load the config for the requested module from the database.
Get method, which returns a specified config value.
Set method, which sets the config value in memory.
Save method, which writes the config update(s) back to the DB.
Testing the Get/Set methods is straightforward; they map directly to private array, and work pretty much as you'd expect.
The problem I have is with testing the database handling. The class uses a number of fields on the config table (module name, language, etc) to determine which config items to load and in what priority. In order to do so, it constructs a series of elaborate SQL strings, and then makes direct calls to the DB to get the correct config data.
I have no idea how to go about writing a unit test for this. Aside from the Get/Set methods, the class consists pretty much entirely of building SQL strings and running them.
I can't see a way to test it sensibly without actually running it against a real DB, and all the issues that go with that -- if nothing else, the complexity of the config loader would mean I'd need at least seven or eight test databases populated with slightly different config. It seems like it would be unmanageable and fragile, which would defeat the point somewhat.
Can anyone suggest how I should proceed from here? Is it even possible to unit test this kind of class?
Many thanks.
I must say I'm not sure I agree that unit tests would be somewhat pointless without hitting the database here. My goal would be to get the business logic that produces the SQL under test, without involving your database. Here is an example of what I'm talking about:
class Foo {
// ... Getters and setters for your config ...
public function doSomeBusinessLogicThenHitDb()
{
$sql = 'SELECT * FROM mytable WHERE ';
$sql .= $this->_doSomethingComplicatedThatInvolvesParsingTheConfig();
$this->_queryDb($sql);
}
protected function _queryDb($sql)
{
// Do something with a PDO or whatever
}
}
Having abstracted the _queryDb() bit to a separate function, you can then write this test:
public function testMyClassUnderSomeCircumstances()
{
// Set up config
$exampleConfig = // whatever
// Set up expected result
$whatTheSqlShouldLookLikeForThisConfig = 'SELECT ... WHERE ...';
// Set up a partial mock that doesn't actually hit the DB
$myPartialMockObject = $this->getMock('Foo', array('_queryDb'), array(), '');
$myPartialMockObject->expects($this->once())
->method('_queryDb')
->with($whatTheSqlShouldLookLikeForThisConfig);
// Exercise the class under test
$myPartialMockObject->setConfig($exampleConfig);
$myPartialMockObject->doSomeBusinessLogicThenHitTheDb();
}
The point of this test is to test the business logic that produces your SQL - not to test your database itself. By putting the expectation in place that the resulting SQL must look like whatever it must look like, you are ensuring that your tests will fail if innocent refactoring of _doSomethingComplicatedThatInvolvesParsingTheConfig() accidentally breaks your code, by making it produce different SQL from what it used to.
If testing the whole application, including its database, is your goal, try a proper integration testing suite like Selenium. Unit tests monitor individual classes and tell you when they've stopped behaving as they're supposed to. You will face problems with speed of execution, and with error localisation (i.e. is the bug even in the code, let alone the class under test, or is it a DB thing?), if you let them overreach.
One straight forward to better test these things is to give your config class an object to access the database, so you can run any config with the real database or just some mock of it that writes to memory instead or even lightweight files if you need persistence for example.
That can be done with creating an adapter with a defined interface. The first adapter you write is for your database.
When the config object is created, you pass in the adapter. As it has a defined interface, the config class can work with any adapter that has that interface. First of all the database.
Then you either mock an adapter or write an adapter of it's own for your tests. Inside your tests, you don't use the database adapter, but the test adapter.
You then can unit-test the config class independent of the database.
Well, as in the title, I can think of three ways to manage testing the database output (I'm using ORM in my application, and PDO in unit tests). Which is the best one? How do you handle this?:
Create data set with the data I want specifically for testing, and change the code so that it reads xml instead of the ORM arrays (in tests classes).
Create setUp() method, set the attribute containing the ORM array, and work on that.
Same as the second point, but with another database, created specifically for testing
You may want to read PHPUnit's chapter on database testing.
I use PDO via my own thin wrapper that supports nested transactions via save points. In the bootstrap, I create a test database with the entire structure of production along with very basic seed data. During each setUp() and tearDown() I begin a transaction and then roll back.
Each test imports the subset of data it needs from raw SQL files. From there, the ORM is tested using real inserts, etc. But this only works because my DB driver supports nested transactions. If the tests begin/commit and check for success/failure, everything still works.
If you don't have nested transaction support, you could set up and tear down the entire database on each test, but that will be slower. And note that you don't always have to test against a real database... it depends on what sort of things you are testing.
In my tests, I use a test database.
MySQL has a few test databases on their site. I find the Sakila rather difficult, so I use the World database.
I'm in the process of testing a factory class.
One of the methods must load the data in an array for an object that another method will instantiate.
That method contains the SQL query that holds a critical condition that must be tested. ( in this case only ask for the records that are "published". Ex.: WHERE published=1 ). That distinction in the SQL Query is the only detail that makes that method differ from another one, and I want to test the query execution behaviour.
Now, I can't really mock my PDO object and ask it to return a fixed result as I would not test the execution of the query by mySQL. That would make a useless test.
That leads me to think that I'll need to set up a static database with fixed test data inside it. Am I right on this or have I missed something?
Should I separate the test requiring the "test database" from the tests that are autonomous ?
I strongly agree on the part of not mocking out the PDO. At some point i want to make sure my queries work against a real database. While this might not be a unit test anymore, technically. For me it offers so much more to know that my code that handles data storage really works against the db.
What i tend to do is creating a sort of Data Access class for each Class that needs to talk to the DB and so separating out most of the business logic from the db access code.
That way i can mock out the data access when testing the classes and after that set up a "Test Database" for each "data access class" and see if those work.
#zerkms Answer (+1) already linked http://phpunit.de/manual/current/en/database.html and the only other resource i have found to be of value when it comes to DB-Testing is the Book Real-World Solutions for Developing High-Quality PHP Frameworks and Applications that has a big chapter covering this subject.
Should I separate test requiring the "test database" from the tests that are autonomous ?
Only if your testsuite gets really really big and you have runtime issues that force you to say "hitting even a testing database just takes to long for all my tests so i run those only on a continuous integration server and not while developing.
Yes, it is the common practice.
It is also the special methods for loading fixtures: http://phpunit.de/manual/current/en/database.html
What about second question: no, you should not separate them. Your tests should test the behaviour, not the details of implementation. Join test cases into test classes by logic.