Goodbye, CouchDB

May 10th, 2012 by Steven Hazel

Here at Sauce Labs, we recently celebrated the completion of a significant project to improve our service uptime and reliability, as we transitioned the last of our CouchDB databases to MySQL. We’d outgrown CouchDB, to the point that a majority of our unplanned downtime was due to CouchDB issues, so wrapping up this migration was an important milestone for us.

CouchDB was a very positive experience at first, and its reliability isn’t out of bounds for a database that is, after all, only on version 1.2. But our service is very sensitive to reliability issues, and ultimately we decided that this transition was the most efficient path forward for us.

Once we decided on MySQL (specifically, we’re now using Percona, with its InnoDB-based XtraDB storage engine), we rearchitected our DB abstraction layer and one by one migrated all our databases, large and small. Our uptime was dramatically improved over the past couple months as we worked through the migration, and performance was slightly improved in the bargain.

This post describes our experience using CouchDB, and where we ran into trouble. I’ll also talk about how this experience has affected our outlook on NoSQL overall, and how we designed our MySQL setup based on our familiarity with the positive tradeoffs that came with using a NoSQL database.

First, how did we get into trouble?

Everything Was Going to be Great

When we first started Sauce Labs back in 2008, we thought we were building something very different from the service we run today. We were excited to try a NoSQL db, having spent too many years using MySQL in ways that the designers of relational databases never imagined. CouchDB seemed well suited to our needs.

Our original product design featured a REST API for storing data on behalf of our customers, and the plan was to drop almost straight through to CouchDB’s already RESTful API. This let us get a prototype up and running in a hurry. It didn’t matter that CouchDB was new and not yet hardened by a lot of real-world usage, we thought, because our database I/O needs were meager, our app was naturally horizontally scalable, and our product was fault-tolerant. We could easily bridge any reliability gap just by keeping replicas and retrying things when they failed. What could go wrong?

What Could Go Wrong

As our little company grew, and we learned about the problems our customers faced, our product underwent several major changes. It made less sense over time to partition data so strictly by user. We came to rely more on database I/O performance. In general, we found ourselves using CouchDB very differently from how we’d originally imagined we would, and much more the way most web apps use databases. That was still a reasonable way to use CouchDB, but the margin of safety we thought we had when choosing it slowly evaporated as our product evolved. And, as it turned out, we needed that margin of safety.

Sauce Labs’ service is more sensitive to reliability issues than the average web app. If we fail a single request, that typically fails a customer’s test, and in turn their entire build. Over time, reliability problems with CouchDB became a serious problem. We threw hardware at it. We changed the way we used CouchDB. We changed our software to rely much less on the database and do much less database I/O. Finally, we decided the best next step was to switch.

Again, none of this speaks particularly badly about CouchDB. It’s a young database, and reliability and performance issues are to be expected. And in a way it’s too bad, because we have no love for classical relational databases. We’re convinced that NoSQL is the future. We’re just not convinced it’s the present.

Some Things We Really Liked about CouchDB

  • No schemas. This was wonderful. What are schemas even for? They just make things hard to change for no reason. Sometimes you do need to enforce constraints on your data, but schemas go way too far. With CouchDB, adding new fields to documents was simple and unproblematic.
  • Non-relational. Relational databases grew up solving problems where data integrity was paramount and availability was not a big concern. They have a lot of features that just don’t make sense as part of the database layer in the context of modern web apps. Transactional queries with 6-way joins are tempting at first, but just get you into trouble when you need to scale. Preventing them from day one is usually easy.
  • No SQL. It’s 2012, and most queries are run from code rather than by a human sitting at a console. Why are we still querying our databases by constructing strings of code in a language most closely related to freaking COBOL, which after being constructed have to be parsed for every single query?

    SQL in its natural habitat

    Things like SQL injection attacks simply should not exist. They’re a consequence of thinking of your database API as a programming language instead of a protocol, and it’s just nuts that vulnerabilities still result from this poorly thought out 1970s design today.
  • HTTP API. Being able to query the DB from anything that could speak HTTP (or run curl) was handy.
  • Always-consistent, append-only file format. Doing DB backups just by copying files was simple and worry-free.
  • Javascript as a view/query language was familiar and useful.
  • Indexes on arbitrary calculated values seemed like a potentially great feature. We never ran into a really brilliant way to use them, though it was straightforward to index users by email domain name.
  • Finally, it’s worth pointing out that even under stress that challenged its ability to run queries and maintain indexes, CouchDB never lost any of our data.

The Problems We Encountered with CouchDB

Availability:

  • In our initial setup, slow disk performance made CouchDB periodically fail all running queries. Moving to a much faster RAID setup helped, but as load increased, the problems came back. Percona is not breaking a sweat at this load level: our mysqld processes barely touch the CPU, we have hardly any slow queries, the cache is efficient enough that we’re barely doing disk reads, and our write load is a very comfortably small percentage of the capacity of our RAID 10 arrays.
  • Views sometimes lost their indexes and failed to reindex several times before finally working. Occasionally they’d get into a state in which they’d just reindex forever until we deleted the view file and restarted CouchDB. For our startup, this was agony. Surprise reindexing exercises were the last thing we needed as a small team already taking on a giant task list and fighting to impress potential big customers.
  • Broken views sometimes prevented all views from working until the poison view file was removed, at which point view indexing restarted its time-consuming and somewhat unreliable work. I don’t know how many times one of us was woken up by our monitoring systems at 4am to learn that our service was down because our database had suddenly become a simple key/value store without our permission.
  • Compaction sometimes silently failed, and occasionally left files behind that had to be removed to make it work again. This led to some scary situations before we tightened up our disk usage alarms, because we discovered this when we had very little space left in which to do the compaction.
  • In earlier versions, we ran into three or four different bugs relating to file handle usage. Bug reports led to quick fixes for these, and these problems were all gone by version 1.0.2.

Performance:

  • There’s really only one thing to say here, and that’s that view query performance in CouchDB wasn’t up to the level of performance we’d expect from roughly equivalent index-using queries in MySQL. This was not a huge surprise or a huge problem, but wow, a lot of things are quicker now, and our database machines are a lot less busy.

Maintenance headaches:

  • When CouchDB fails, it tends to fail all running queries.  That includes replication and compaction, so we needed scripts to check on those processes and restart them when necessary.
  • View indexes are only updated when queried — insertion does not update the index.  That means you have to write a script to periodically run all your views, unless you want them to be surprisingly slow when they haven’t been queried in a while. In practice we always preferred view availability to any performance boost obtained by not updating indexes on insertion, but writing reliable scripts to keep view indexes up to date was tricky.
  • The simple copying collector used for compaction can spend a lot of time looking at long-lived documents. That’s particularly bad news when a database has both long-lived and short-lived documents: compaction takes a long time, but is badly needed to keep disk usage under control. Plus, you have to run compaction yourself, and monitoring to make sure it’s working is non-trivial. Compaction should be automatic and generational.

Unfulfilled promise:

  • CouchDB’s design looks perfect for NoSQL staple features like automatic sharding, but this is not something it does.
  • What is the point of mapreduce queries that can only run on a single machine? We originally assumed this feature was headed toward distributed queries.
  • It was never clear to us what the CouchDB developers considered its core use cases. We saw development focus on being an all-in-one app server, and then on massive multi-direction replication for mobile apps. Both interesting ideas, but not relevant to our needs.

(We’re told that a few of these issues have already been addressed in the recently-released CouchDB 1.2.)

We were able to work with CouchDB’s performance, and over time we learned how to script our way around the maintenance headaches. And while we were worried that CouchDB seemed to be gravitating toward use cases very different from our own, it was the availability issues that eventually compelled us to switch. We talked about a number of possible choices and ultimately settled on a classic.

MySQL, the Original NoSQL Database

So why not switch to another document-oriented database like MongoDB, or another NoSQL database? We were tempted by MongoDB, but after doing some research and hearing a number of mixed reviews, we came to the conclusion that it’s affected by a lot of the same maturity issues that made CouchDB tough for us to work with. Other NoSQL databases tended to be just as different from CouchDB as MySQL — and therefore just as difficult to migrate to — and a lot less well known to us. Given that we had experience with MySQL and knew it was adequate for our needs, it was hard to justify any other choice.

We’re familiar with MySQL’s downsides: among other things, it’s terrible to configure (hint: the most important setting for performance is called innodb_buffer_pool_size), and its query engine, besides being SQL-oriented, guesses wrong about how to perform queries all the time. Experienced MySQL users expect to write a lot of FORCE INDEX clauses.

The InnoDB storage engine, on the other hand, is pretty great overall. It’s been hardened by heavy use at some of the biggest internet companies over the past decade, dealing with workloads that resemble those faced by most modern developers. At the lowest level, almost any database is built on the same fundamentals of B-trees, hashes, and caching as InnoDB. And with respect to those fundamentals, any new database will have to work very hard to beat it on raw performance and reliability in real-world use cases. But maybe they won’t all have to: Percona’s forward-thinking key/value interface is a good example of how the solid InnoDB storage engine might make its way into true NoSQL architectures.

In switching to MySQL, we treated it as much like a raw storage engine as we reasonably could. So now we’re back to using MySQL in the way that inspired so much NoSQL work in the first place:

  • We ported our CouchDB model layer to MySQL in a way that had relatively minor impacts on our codebase. From most model-using code, using MySQL looks exactly the same as using CouchDB did. Except it’s faster, and the DB basically never fails.
  • We don’t use foreign keys, or multi-statement transactions, or, so far, joins. When we need to horizontally scale, we’re ready to do it. (But it’ll be a while! Hardware has gotten more powerful since the days when sharding was invented, and these days you can go a long way with just a single write master.)
  • We have a TEXT column on all our tables that holds JSON, which our model layer silently treats the same as real columns for most purposes. The idea is the same as Rails’ ActiveRecord::Store. It’s not super well integrated with MySQL’s feature set — MySQL can’t really operate on those JSON fields at all — but it’s still a great idea that gets us close to the joy of schemaless DBs.

It’s a nice combination of a proven, reliable database storage engine with an architecture on top of it that gives us a lot of the benefits of NoSQL databases. A couple months into working with this setup, we’re finding it pretty hard to argue with this best-of-both-worlds approach.

Share

Integrating OpenShift with Sauce OnDemand

May 2nd, 2012 by Ross Rowe

OpenShift is Red Hat’s Cloud Computing Platform as a Service (PaaS) offering. It provides the infrastructure to let you run your applications (Node.js, Ruby, Python, PHP, Perl, and Java). Integrating OpenShift applications with Sauce OnDemand is really easy, so you can run your applications and your tests in the cloud within minutes.

The instructions below cover Java applications built with Maven deployed under JBoss.

Firstly, follow the instructions on the OpenShift Getting Started page to create an OpenShift account, create a SSH key and install the client tools.

Once this is done, you can create a namespace for your application by running the following:

rhc domain create -n YOUR_NAMESPACE -l YOUR_OPENSHIFT_LOGIN

Then create a JBoss application with Jenkins enabled:

rhc app create -a YOUR_APP -t jbossas-7 --enable-jenkins

This will create a Git repository, and both a JBoss and a Jenkins instance for your application.

We’ve created a fork of the JBoss OpenShift Kitchen Sink example to demonstrate how you can write a Selenium test to run against an OpenShift instance.

The fork includes some key additions to the Maven pom.xml file, namely the inclusion of the following dependencies:

	<!-- Selenium dependency -->
	<dependency>
		<groupId>org.seleniumhq.selenium</groupId>
		<artifactId>selenium-java</artifactId>
		<version>2.13.0</version>
		<scope>test</scope>
	</dependency>
	<!-- selenium-client-factory dependencies -->
	<dependency>
		<groupId>com.saucelabs.selenium</groupId>
		<artifactId>sauce-ondemand-driver</artifactId>
		<version>2.4</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>com.saucelabs.selenium</groupId>
		<artifactId>selenium-client-factory</artifactId>
		<version>2.4</version>
		<scope>test</scope>
	</dependency>

The pom.xml file also includes a new ‘arq-jbossas-openshift’ profile, which can be used to run the Kitchen Sink unit tests against the OpenShift instance.

<profile>
	<id>arq-jbossas-openshift</id>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-surefire-plugin</artifactId>
				<configuration>
					<systemPropertyVariables>
						<arquillian.launch>openshift</arquillian.launch>
					</systemPropertyVariables>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.jboss.arquillian.container</groupId>
			<artifactId>arquillian-openshift-express</artifactId>
			<version>1.0.0.Beta1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</profile>

The org.seleniumhq.selenium dependency will add the Selenium classes to the classpath, and the com.saucelabs.selenium dependencies will add the selenium-client-factory library, which provides some helper classes to construct the Selenium/WebDriver instances so that they can connect to Sauce OnDemand.

The org.jboss.as.quickstarts.kitchensink.test.MemberRegistrationIntegrationTest class demonstrates how to write a Selenium test to run against an OpenShift instance. It is basically the same as any other Selenium test, but it uses the selenium-client-factory library. The class relies on some environment variables to be populated, which provide details of the Selenium host, port and starting URL. This is done so that the class can easily be integrated with the Sauce OnDemand plugins for your choice of continuous integration build (eg. Jenkins, Hudson or Bamboo).

In addition, the org.jboss.as.quickstarts.kitchensink.test.MemberRegistrationArquillianTest class demonstrates how to write an Arquillian-enabled Selenium test to run against an OpenShift instance.

In order to use Arquillian for your tests, you will also need to modify the src/test/resources/arquillian.xml file to reflect your OpenShift instance.  The relevant section of the arquillian.xml file to modify is:

<container qualifier="openshift">
<configuration>
<property name="namespace">YOUR_OPENSHIFT_NAMESPACE</property>
<property name="application">YOUR_OPENSHIFT_APPLICATION</property>
<property name="sshUserName">YOUR_OPENSHIFT_UUID</property>
<!-- Passphrase can be specified by defining the environment variable SSH_PASSPHRASE -->
<property name="login">YOUR_OPENSHIFT_LOGIN</property>
</configuration>
</container>

To update your OpenShift project to use the forked Kitchen Sink example source code, run the following from your project’s directory:

git remote add upstream -m master git://github.com/saucelabs/kitchensink-example.git
git pull -s recursive -X theirs upstream master

To deploy the updated code, simply run the following

git push

That’s it, the Kitchen Sink example should now be available from http://YOUR_APP-YOUR_NAMESPACE.rhcloud.com.

Now you can run the integration tests! You can run them in your local environment by executing:

mvn -Dsauce.user=YOUR_SAUCE_USER -Daccess.key=YOUR_SAUCE_KEY \
-DSELENIUM_STARTING_URL=http://YOUR_APP-YOUR_NAMESPACE.rhcloud.com \-Parq-jbossas-openshift test

You can also configure a Job in your OpenShift Jenkins instance to run the integration tests. To do this, first install, configure and enable the Sauce OnDemand plugin for Jenkins, then you can create a new Job in Jenkins to run the Maven build.

As a bonus, for any OpenShift users who open a free Sauce account and run at least one KitchenSink test as outlined above, we’ll fill their account with the equivalent of three months of our “small team” plan. That’s 3000 minutes, which is 50 hours of free testing!

So to qualify for three months free Sauce “Small Team” balance:

  • create an account on OpenShift,
  • create a matching account on Sauce Labs
  • enter the ‘RedSauce’ promo code at the time of account creation and
  • run at least one test following the template
  • email the link to the job video from your test to sales [at] saucelabs [dot] com and we’ll set you up!
Share

Flying Saucers Are Off To London & Germany for SeConf, Hamburg.JS & Berlin.JS

April 13th, 2012 by Ashley Wilson

A couple of us Saucers are off to London today for the 2nd annual Selenium Conference, which we are thrilled to be sponsoring and speaking at. As one of the volunteer organizers, I personally can attest to what an awesome couple of days it will be (no biases or anything :P). With talks by Selenium project committers and community members alike, this conference is a fantastic way to hear what others are getting out of the tool, network with some of the brightest dev and tester-focused folks, and get an inside look at the present and future direction of the project.

If you’re attending too, make sure to pop by our sponsor table for some seriously tasty hot sauce to take home with you (3oz, airplane-check-in approved). And check out the talks by Sauce co-founder Jason Huggins, Director of Web Development Adam Christian, and Sauce Ninja Santiago Suarez Ordoñez, who will cover using Robots for mobile testing, the exciting future of Selenium Builder, and tips for speeding up your Selenium tests.

After London, we’re sticking around this side of the pond and making our way to Germany, where we’ll be joining the JS crews in Hamburg and Berlin for two Sauce / Selenium meetups. There are still spots left so join us if you can! We’re also available for tech talks, beers, etc so if you’re interested in chatting further about Selenium & Sauce, just let us know.

RSVP Info

Hamburg.JS, April 23 at 7pm: BBQ Sauce Special
We will shift the meetup a week and give a warm welcome to the guys from Sauce Labs – sponsor of JSConf.us! And to spice up the event even more, we are going to have beer and barbeque in the backyard of SinnerSchrader.

Berlin.JS, April 24 at 6:30pm: Selenium & Cloud-Based Testing
If you’ve given any thought to automated web testing, then this is the Berlin.JS meetup for you. Join us April 24th for a special event co-hosted with Sauce Labs as they discuss Selenium and the benefits of cloud-based automated testing.

Cheers!


Share

#SFSE Video: Stripping Down RemoteWebdriver

March 13th, 2012 by Ashley Wilson

For our February San Francisco Selenium Meetup, Santiago Suarez Ordoñez, Sauce Ninja and Selenium Contributor, dove in to the RemoteWebdriver codebase and emerged with a highly technical talk that covered everything from the DesiredCapabilites object to binding implementations and caveats.

For those unfamiliar, RemoteWebdriver lets you run your Webdriver tests remotely and use Sauce Labs or Selenium Grid to scale your testing. In Santi’s opinion, it’s the best driver out there, and if you have a look at the video below, you’ll get a sense as to why that is. Watch along as he gives a brief overview of what RemoteWebdriver is before going into its architecture design, the JSONWireProtocol, and the pros and cons for using it over other drivers.

Thanks to our friends at Huddler Inc for hosting AND providing ample pizza and beer to keep us happy for the night. To learn more about the San Francisco Selenium Meetup group and attend a future event (we’ve got one tomorrow night!) visit our meetup page. And if you’re interested in presenting at or hosting a meetup, please get in touch!

Share

Selenium Tips: Uploading Files in Remote WebDriver

March 8th, 2012 by Santiago Suarez Ordoñez

Since it’s been a while since my last Selenium tips blog post, I thought it was time to share some Selenium love again. Today we’re covering WebDriver’s native solution to a very common issue when doing distributed cross browser testing: uploading files in remote servers.

As you may know, the way to address this in Selenium 1 is to place your files in an accessible web server and use the attachFile command that points to the correct URL. With Selenium 2, the Selenium Dev team has luckily made this a lot more straightforward.

For those of you doing this locally, all you need to do is use the sendKeys command to type the local path of the file in any file field. This works like a charm in all drivers. When moving this test to a remote server (such as, for example, our Selenium 2 Cloud), all you need to do is use the setFileDetector method to let WebDriver know that you’re uploading files from your local computer to a remote server instead of just typing a path. Almost magically, the file will be base64 encoded and sent transparently through the JSONWireProtocol for you before writing the fixed remote path. This is an excellent solution, as it lets you switch your tests from a local to remote Driver without having to worry about changing your tests’ code.

This feature is available in all the official Selenium 2 bindings, just make sure Selenium 2.8.0 or newer is used as this feature has been released then. Here are some examples tests:

Java
Ruby

And here’s what the test looks like after running in our cloud:
https://saucelabs.com/jobs/1a408cf60af0601f49052f66fa37812c

Pro tip: Write your own FileHandler that will use special codes to represent files before typing paths (locally) or uploading them (remotely). Example: “test-file:small-image”

Share

Selenium, Jenkins, Robots, Oh My!

February 16th, 2012 by Ashley Wilson

If you’re planning to be in San Francisco this March 14th (right after PyCon!), join us for an awesome free event we’re co-hosting with CloudBees and Eventbrite that showcases and celebrates the ways Jenkins and Selenium go hand in hand.

Selenium, Jenkins, Robots, Oh My! will features talks by Kohsuke Kawaguchi, creator of Jenkins; Jason Huggins, creator of Selenium; and John Shuping & Theo Cincotta, Principal & Sr. Systems Engineers at Eventbrite. You’ll get an update on the Jenkins & Selenium 2 integration, a second opportunity to see how a certain Angry Birds-playing robot is doing, and an inside look at how Eventbrite uses Selenium and Jenkins in production.

The event kicks off at 6pm PST with plenty of beer and food, with the talks at 6:30. We’re planning to wrap up around 8pm so if you need to take the Caltrain you won’t be in a rush. Oh, and this will be recorded, so if you can’t make it, you don’t have to miss out totally :-)

To RSVP, please visit our Eventbrite page and register for a ticket. See you there!

Share

Announcing Support For Selenium 2.18.0, Firefox 10.0, Chrome 16.0.912.77 and Opera 11.61

February 10th, 2012 by Santiago Suarez Ordoñez

As you all know, the Selenium team moves crazy fast and we do our best to keep up.

This time, we have the pleasure to announce we now support Selenium 2.18.0! This releases is packed with bug fixes and improvements. Here’s the changelog for more information.

Along with the Selenium upgrade, we included upgrades to out browsers. That includes Firefox 10.0, Chrome 16.0.912.77 and Opera 11 for both Selenium 1 and Selenium 2 users. To use these browsers, make sure you specify 2.18.0 as the Selenium version to use, as our current default (2.6.0) won’t support them.

You can start using this new versions right now by adding the following Desired Capabilities/JSON key-value:

"selenium-version": "2.18.0"

During the next few weeks we’ll testing and considering moving our default version to this version. If you see any issues after moving your tests to it, we definitely want to hear about it.

For more information about the current Selenium version that is used by Sauce, how to use other Selenium versions, and the selenium-version flag, have a look at our docs in the Sauce OnDemand additional configuration section.

Share

Getting Started With Web Testing Using Selenium & Sauce Labs

February 9th, 2012 by Will Iverson

This is a guest blog post by Will Iverson, David Drake, Osama Khalaf, and Adam Herbst of Dynacron Group, a technology consulting firm based outside Seattle that provides software development (Java/.NET) and BI/DW services based on Continuous Delivery.

Dynacron Group staff started working with Selenium nearly four years ago. The biggest problem we had was execution time – we had a limited number of browsers and during launches, test & release jobs would start to pile up as we were waiting for the limited browsers in our homebrew Selenium grid to be available.

To solve that problem for a new project we started almost a year and a half ago, we decided to use Sauce Labs. To make it as easy as possible for the test team, we created a parallel execution framework to make it dead easy to run the tests, and a template project to make it dead simple to get started.

By standardizing the parallel execution framework and template project, a tester can pretty much just sign up with Sauce Labs, follow a few basic configuration steps, and start writing test suites that leverage parallel execution in no time. Most people start with the Selenium Recorder, just pasting commands in from the clipboard to start and then pretty quickly graduate to just writing stuff in their IDE of choice.

Let me explain why parallel execution is so cool: we write 200 tests. We run those tests in parallel across five browsers (Firefox, Chrome, Safari, and two versions of Internet Explorer). That’s a thousand tests. If we ran those serially, one after the other, it would take the better part of a day. By running fifty parallel browsers, we can finish that test suite in fifteen minutes.

The Sauce Labs site can give you an overview of the other awesome features (recording video of every job, step-by-step screenshots, and Sauce Connect to name a few) – but nothing is as nice for us as the time-to-market advantage.

For more information, check out:

Tutorial

Just download this tutorial and follow the instructions to get started.

webtest-quickstart

Webtest Quickstart is the template project. This is where you’ll want to go to get started quickly.

If you’re just starting on a new Selenium test project, you might consider using JUnit to manage your test cases. The webtest-quickstart project can provide a simple starting point for importing your IDE tests into JUnit and running them in maven.

To get started, just download the the webtest-quickstart project from github, or use the maven archetype from the same site. Inside the project, you’ll find sample test cases for many different testing situations. There are samples for running tests against a site running on your computer, or against websites on your network. Best of all, the parallel-webtest library gives you the ability to easily configure how tests are run. The project pom has sample profiles for running your tests in your own browser or in several different Sauce browsers in parallel, all from the same test code.

parallel-webtest

Parallel Webtest is the library that handles the parallel execution & configuration.

At its heart is a JUnit base test class that manages the browser lifecycle. It configures and launches the browser before executing a test class, and cleans it up afterwards. This isolates the test code, promoting cleaner test methods. Browser selection and parallel configuration are handled with a few configurable system properties; the same test code can be run in serial or parallel, locally or remotely, against one browser type or many. As a result, a test suite that once took four hours to run can be finished in as little as fifteen minutes using the same build server.

The webtest-quickstart and webtest-quickstart-archetype are open-source. Both are available on Dynacron Group’s Github account. If you have a chance to try it out, let us know what you think at info@dynacrongroup.com. And happy (faster) testing!

Share

#SFSE Meetup Video: Keeping Selenium Tests 100% Blue

January 31st, 2012 by Ashley Wilson

The 2012 San Francisco Selenium Meetups kicked off last week with a great talk by Denali Lumma (@denalilumma), who is the QA Lead at Okta.

Okta, an on-demand identity & access management service for cloud/SaaS applications, is notable for that fact that they manage to keep 1,000 Selenium Tests 100% blue – and they don’t use any manual testers. To get an inside look at how they accomplish this impressive feat, take a look at the video below.

For more info about the San Francisco Selenium Meetups and to join the group, visit our meetup.com page. And for help with starting a Selenium Meetup in your own city, check out my blog post on the subject :-)

Share

GivenWhenThen: Simple, Powerful Acceptance Testing for Node.js

January 25th, 2012 by Doug Wright

Over the last year or so, interest in and development for Node.js has accelerated dramatically. There is no shortage of excitement and promise in this burgeoning community and for many of the same reasons cited by others, about six months ago we decided it made sense to dive in ourselves.

Coming from a committed testing background on multiple web frameworks, including Rails, we are strong adherents to BDD. Naturally the first thing we did when spinning up on the Node stack was look around for good BDD acceptance testing tools. Not finding anything that fit for us, we went ahead and rolled our own. In this post, we’ll introduce GivenWhenThen, a DSL and runner that allows anyone to easily construct acceptance tests with BDD semantics in straightforward, sentence-like statements and then run them against Sauce Labs.

Writing a Story

Although you can also directly access the Selenium RemoteWebDriver that is under the covers, GivenWhenThen is primarily intended to provide a DSL for writing executable stories in the Dan North format. To write a story, begin with a BDD description in a xxx_test.coffee file:

 

story 'Executing a Google search',
  """
  As a human
  I want to perform a search
  So that I can access the world's information
  """, ->

 

And then add one or more scenarios:


scenario "Search for info about Node.js", (browser) ->
  browser
    .given "I am on the homepage", ->
      browser.step(steps.visitHomepage)
    .when "I enter search terms", ->
      browser.typeInElement('q', 'nodejs', using:'name')
    .and "submit the search", ->
      browser.clickElement('btnG', using:'name')
    .then "I see search results", ->
      browser.assertTextPresent('results')
    .and "the results contain information about nodejs", ->
      browser
        .assertTextPresent('node.js')
        .assertTextPresent('nodejs.org')

 

Each scenario has “given“, “when“, and “then” steps.
  • Given: Set up the initial conditions for the scenario.
  • When: Take the action the scenario is testing.
  • Then: Assert the conditions expected after taking the tested action.

Each step contains one or more chained calls to WebDriver commands in the form of browser.someCommand.

Each step (given, when, then) can have an arbitrary number of and steps following it (see above example).

Steps

Often there are steps that are repeated throughout many scenarios, for example “visit homepage” or “sign in”. This kind of functionality can be defined in steps and referred to in scenarios via browser.step():

 

scenario "Search for info about Node.js", (browser) ->
  browser
    .given "I am on the homepage", ->
      browser.step(steps.visitHomepage)

 

Steps are defined in *_steps.coffee files. Multiple steps per file can be defined as follows:

 

steps.visitHomepage = (browser) -> browser.get 'http://www.google.com'

 

Multiple steps files can be created to organize your steps in a manageable way.

Configuration

Configuration is simple and contains:
  • Overall story and Sauce Labs configuration.
  • Browser / OS definitions. Stories will be run against each browser/os configuration defined.

For example:

config.credentials =
  'username':   'sauce_labs_username'
  'access-key': 'sauce_labs_access_key'

config.settings =
  'max-duration': '180'

config.browsers = [
  {
    'platform':     'VISTA'
    'browserName':  'firefox'
    'version':      '7'
  }
  {
    'platform':     'LINUX'
    'browserName':  'firefox'
    'version':      '7'
  }
]

 

Subtitles In Test Videos

One of our favorite features of Sauce Labs is test videos. It is incredibly useful to be able to view a video of a failed test and quickly understand what went wrong. However, one issue that has been raised about the videos is that they could sometimes benefit from some context about what is happening on screen.Well, it just so happens that this context is conveniently codified in the steps of BDD scenarios! So, by flipping a switch in GivenWhenThen, you can add BDD subtitles to your Sauce test videos. When the subtitle flag is passed to the GivenWhenThen runner, subtitle divs will be inserted into the test browser, which will show up in the Sauce Labs test videos and illustrate which of the BDD steps is currently being executed:

 

 

Design Background

In addition to being built on top of Sauce Labs and Selenium 2, GivenWhenThen is heavily influenced by the BDD movement.

While it is also clearly influenced by Cucumber, aficionados will surely notice differences. Most obviously, in GivenWhenThen there is no prose-to-code translation step. Cucumber uses the Gherkin language, which parses the prose into features, scenarios, and steps.

In GivenWhenThen, the DSL is embedded directly in the host language. We do realize there is real benefit to the full Cucumber vision – including Gherkin – but we took a leaner approach to getting the BDD structure without completely porting Cucumber.  To us, the core power of the BDD approach comes from the conceptual framework Dan North developed. Just stating the story and scenario in that formal way and using the “given, when, then” flow gives powerful structure and coherence to your specifications.

Conclusion

GivenWhenThen fills a need we have by providing a Selenium 2 driver and Sauce Labs integration for Node.js, and adding BDD structure to our tests, while staying light and keeping us close to the host language. We hope you find it useful as well!
Share