I used Propel ORM to duplicate a table schema, in order to do continuous integration, but Propel only gets me a fully fleshed out schema, it doesn't get me test data (or basic necessary data at all).
How do I get the data from a live/test database with a version controlled propel-gen
Propel ORM ecosystem?
They say that "best practice" in anything at all doesn't exist - it's so subjective that one ought to settle for one of several forms of "good practice" instead. I think the below qualifies for that label — and ultimately it works well for me. I've been using PHPUnit for about a year, and perhaps for six months on my projects from scratch.
Here's a synopsis of what I do in the PHPUnit bootstrap phase (specified in
phpunit.xml
):myproject_test
databaseinsert-sql
Propel command on a pre-migrations copy of the generated SQLmigrate
Propel commandThe benefit of inserting SQL manually and then running migrations is that migrations get a really thorough testing. This is especially handy since in development I sometimes will do a
down
, modify a migration class, then do anup
to re-run it: it is therefore reassuring to know it will run in order. At present I plan to keep all of my migration history permanently; whilst it will add very minor delay to testing and new builds, upgrade deployments won't be affected.Since my build depends on having an old SQL file, I avoid using the
sql
generation command; if it is accidentally issued, the modified SQL files can be trivially reverted in version control.At present, I am simply using a database name of
myproject_test
onlocalhost
, so that wherever the tests are run, other database are not affected. It is possible that on build servers you will be required to connect using different credentials: consider detecting the machine name in aswitch()
statement, and selecting the connection details accordingly.To give you data to test, I am generally inclined to recommend you don't use an export of data from your live system. There's usually too much of it, for one, and also you generally want to create pieces of data per test, so that tests are completely isolated. I think this is a good idea for two reasons:
This is where my builder classes come in. I use this in my
bootstrap.php
and call it on each folder containing test classes:In
!CommonBuild.php
I add read-only data that won't be modified by tests, and so it is safe to have just one copy.I have one build class per PHPUnit test class: for every
*Test.php
file I have, I will have a corresponding*Build.php
. In each builder, abuild
static method is called, and in that I manually run a method for each test that needs something built. Here is a simple one:At some point in the future I'll probably use Reflection to run these automatically, like PHPUnit does for tests, but it is fine for now.
Now, in my bootstrap script I fully initialise Propel, using the test connection, so ordinary Propel statements are available. I will thus create just the data I need, like so:
I have a naming convention that a test of
testWriteVarToFieldUsingFoundMatch
in a test class gets a builder calledbuildWriteVarToFieldUsingFoundMatch
in the corresponding build class. It's not enforced as such in code, but this naming helps find one given the other easily (I will often edit both at the same time, using my IDE's split screen feature).So, in the example above, I only needed one employer record, one job record, one process record and one source record to run this particular test (and not a whole live export). The source record is given a unique name relating to the test name, so that it will only be used in this test (I've found I have to watch out for copy-and-paste errors here - it is quite easy to use the wrong data in a test!).
Creating test data of this kind is quite easy, whatever kind of database you have:
user.name
fields,address.line1
fields and so forth can usually be created containing a unique identifier, so that when you modify this data in a test, you know that only that test is going to use it, and thus that it is isolated from other tests.I've opted to run all builders in the bootstrap regardless of what tests are being run, for reasons of simplicity. Since this takes only 15 extra seconds, in my case it's probably not worth doing something more complicated. However, if you wish, you could do something clever with the
setUp
method in each PHPUnit test, detect the current test (if possible) and then run the appropriate build class.