I’ve been helping out with the Diaspora project, an open source social network that gives you control over your own data.
When I first started poking around the codebase a few months ago, they’d just started writing a few cucumber selenium integration tests – which of course I want to encourage! – but they weren’t running them on their continuous integration box. And if you aren’t running them on CI, you can’t really call them tests…
It’s not that the developers were lazy; it’s just that Diaspora’s CI box is an ubuntu server instance that doesn’t even have xwindows. There’s no way to attach a display, and without a display, a browser won’t run. So how can you run selenium features that have to actually open a browser window and click on stuff?
I’m glad you asked!
You set up a virtual framebuffer – essentially a simulated display – and run your features in there. Here’s how I got this going.
Step 1: Install Crap
Yeah, you need xwindows, and a bunch of other stuff. I’m not a frequent linux user, so I was surprised that a lot of these commands appeared to fail, with extremely lengthy output about how such-and-such dependency could not be installed, or compiled, or resolved, or kafloozled, or whatever. But then when I ran the programs that supposedly weren’t installed, they worked fine. …thanks, ubuntu?
sudo aptitude install xubuntu-desktop
sudo aptitude install exaile
sudo aptitude install gconf2
sudo aptitude install xvfb
sudo aptitude install firefox
Step 2: Verify Your Virtual Framebuffer
I like the phrase “virtual framebuffer.” It’s very computer science, and using it makes me feel smart. But really, it’s just a display that gets rendered in memory, but not output to any monitor. Programs that require the ability to create windows, such as Firefox, need a display to run in – but they’ll take any old display. If you give Firefox a virtual framebuffer, it’s perfectly happy and treats it like any other display, even though no one can see it.
So let’s verify we have everything set up to run one. This magic incantation starts one on display port :99.
Xvfb :99 -ac -screen 0 1024x768x16
In another shell, run this one, which opens Firefox to example.org inside the virtual display you’re running in the original shell.
DISPLAY=:99.0 firefox http://example.org
Firefox is now open to example.org, the world’s most boring webpage. Really! Let’s prove it by taking a screenshot in a third shell.
xwd -root -display :99.0 -out xwdout
Your screenshot is now in a file called xwdout. Sadly, this is not an image format that normal image viewers can see, so let’s convert it to a jpeg.
convert xwdout screenshot.jpg
Using your favorite sftp program, retrieve screenshot.jpg and have a look at it. It should look something like this:
Ta-da! You have evidence of this so-called “virtual” framebuffer. Now all we have to do is set it up so to start before we run the tests, and stop once the tests are done.
Step 3: Adventures in Shell Scripting
This goes in your /etc/init.d, and will give you ULTIMATE POWER, if ultimate means you can start and stop a virtual framebuffer on display port :99.
Now you just need a rake task that starts the display, passes the port info to the tests, and then stops it.
And just in case you have to get this running with rvm, here’s the shell script that cc.rb calls to kick off a Diaspora build on ruby 1.8.7.
I’ve had to set this up twice, now, but there are a lot of moving parts. If you try this and notice something I forgot, please let me know.
Enjoy your headless cucumbers…
(I wonder what kind of google search results that will get me.)
You could also set up an SSH server and a browser on the headless machine and then use another machine with a display (which may even run Windows or OSX) run an X server on it, SSH into the headless machine and run the browser from the SSH session with the display being rendered on the other machine.
Not that I quite understand why you’d want to run the browser on the server itself apart from timing reasons, which I’d also fail to reasonably be able to measure using a real display…
@Dorian, I wanted something fully automated that I don’t have to interact with. This is for a continuous integration server, which runs the full test suite on the new code every time someone checks in. I want that test suite to include the cucumber features, but I don’t actually care about seeing them run.
[…] xvfb and cucumber collide in Running Cucumber Features Without a Display […]
Why exaile ?
Wow, this is really really good. You are brilliant !
Helped me save tons of ram on multiple servers having to Xorg with WM.
cheers.
Fred
Thank you for a Great Post
This is very helpful
Could Mechanize help automating tests ? It’s a library (there is a version for perl, python, ruby and maybe others) used to simulate classical browsing experience.
Why do we need “export SELENIUM_SERVER_PORT=53809” part? Is 53809 a custom port you are running selenium-webdriver on? By defaults its 4444.
@bikashp, you don’t necessarily need that. I was having trouble starting up and shutting down the old-style selenium that the jasmine gem required, so I just left it running on port 53809 all the time.
Thankfully with the newer versions of the jasmine gem that use webdriver, I don’t need to do that anymore.