Wednesday, November 4, 2009

Getting Testjour up and running - part 2

Under the hood

When I went to figure out how Testjour worked, I really struggled with figuring out how to actually use it since the existing README is a little out-of-date and, as I mentioned before, Google was bereft of any useful information other than "hey we're using Testjour and it cut our test time by X". After spending a day digging through the code, I reached an epiphany and decided to look at the cucumber features. Viola! Using a couple well-placed debugs I was able to figure out the command-line switches and was off to the races. Let's look at a typical usage of testjour:

testjour --on=testjour://slave1/home/user/myapp --on=testjour://slave2/home/user/myapp ./features

As you can see here, testjour is actually an executable which means you must first install testjour on your system. To do this run "rake install" from your testjour directory. This will compile testjour in to a gem and install it in your system. If you do not install this as sudo or root, it will prompt you for your sudo/root password since it must install the executable. If you don't have root access, it should be fairly easy to instead to "rake gem" and install the pkg/testjour-x.x.x.gem and put the testjour executable in your path instead.

What Testjour ends up doing is it rsyncs the directory you run testjour from to the slave1 and slave2 servers under /home/user/myapp (or whatever directory you specify). For this reason, you will want to ensure the data transfer between your master and slaves is as efficient as possible. Testjour runs:

ssh slave1 testjour --in=/home/user/myapp run:remote http://user@master.domain.tld/original/path

and

ssh slave2 testjour --in=/home/user/myapp run:remote http://user@master.domain.tld/original/path

You'll see that it uses ssh without password authentication. This means you must set up key authentication to all your slaves. A healthy Google search can assist you with this if you have never done this before. You'll also see that the master response URL is automatically constructed from the local user you run testjour from, what it thinks its hostname is, and the path you run testjour from. It is recommended that you add all your slaves to the /etc/hosts file on the master and the master to /etc/hosts on all your slaves.

Once the testjour command executes on the slave, it ends up running:

cd /home/user/myapp
testjour run:slave http://user@master.domain.tld/original/path

which runs

rsync -az -e "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" --delete --exclude=.git --exclude=*.log --exclude=*.pid user@master.domain.tld:/original/path/ /home/user/myapp

and then starts running cucumber on all the features in ./features.

This is where your master's application is rsync'd to the slave. It does use rsync over SSH, so you will also need to make sure that your master server allows key authentication from all your slaves.

If you are using sqlite, this should all run fine-and-dandy. Unfortunately testjour has support only for msyql currently so you'll have to modify testjour if you use postgresql or any other server-based db. To get mysql working you will need to re-run the original command as such:

testjour --on=testjour://slave1/home/user/myapp --on=testjour://slave2/home/user/myapp --mysql-create-db ./features

What this does is runs the following commands on your slaves:

/usr/local/mysql/bin/mysqladmin create testrunner_12391987
/usr/local/mysql/bin/mysql testrunner_12391987 < /home/user/myapp/db/development_structure.sql


This requires you to mysqldump your update-to-date development or test database to db/development_structure.sql (mysqldump -u 'username' --password='password' -n -d > db/development_structure.sql). Also you'll notice that the paths are fixed for mysql and mysqladmin. Since this is not where my executables were located, I simply symlinked the original executables to the /usr/local/mysql/bin path.

Another issue you might notice is that Testjour generates a random database name. This was a problem for us since our application requires a specific database name of openphin_test. For this critical reason, I forked Testjour to Dishwasha/testjour and added the --mysql-db-name switch. So we now run the following:

testjour --on=testjour://slave1/home/user/myapp --on=testjour://slave2/home/user/myapp --mysql-create-db --mysql-db-name=my_db_name ./features

The last issue we ran in to was that by default, Testjour will run two instances of cucumber simultaneously on the master. The OpenPHIN project uses bmabey-cleaner to clean the MySQL database before every scenario and each of the tests can easily clobber each other if they are hitting from the same table. Testjour has the switch --max-local-slaves which allows us to set how many cucumber instances to run on the master server. You can run --max-local-slaves=1 to include your master server as one of the cucumber processing slaves or --max-local-slaves=0 to not include the master server in your cucumber processing so the test run only on the slaves. You can also run --max-local-slaves=1 and not specify any slaves to run tests on just the master.

testjour --on=testjour://slave1/home/user/myapp --on=testjour://slave2/home/user/myapp --mysql-create-db --mysql-db-name=my_db_name --max-local-slaves=1 ./features


You can also specify specific feature files or features directories by changing ./features to the appropriate directory.



Back to part 1Continue to part 3

No comments:

Post a Comment