Sunday, June 15, 2014

Behaviour driven testing with Cucumber-JVM and Stubbed Cassandra

So you're tasked with building a service that facades your company's legacy user management system. You need to do this as your part of the company needs to handle the legacy system going down.

To add resiliency you are required to save a user's information in case the legacy system is down. You need to be able to handle restarts so this cache will need to be persistent.

You're going to use Cassandra as your persistence, so how do test this? You sit down your your analyst and QA and come up with the following feature:

Feature: Retrieve User Information
Scenario: User information is retrieved from the company wide repository
Given The company wide user repository is available
When User information is requested
Then The user information is returned
Scenario: Company wide information store is down
Given The company wide user repository is unavailable
When User information is requested
Then Unable to retrieve user information is returned
Scenario: Company wide information store is down but the information has been saved previously
Given The user had been retrieved previously
And The company wide user repository is unavailable
When User information is requested
Then The user information is returned
How would you implement these features? Assuming that the legacy system is a HTTP service you can use Wiremock to mock it being up and down.

For example here is how to mock the legacy system being up with Wiremock:

@Given("^The company wide user repository is available$")
public void setupUserRepository() throws Throwable {
givenThat(get(urlEqualTo("/user/" + id)).willReturn(
aResponse().withBody("{\"firstName\":\"Chris\",\"lastName\":\"Batey\"}")
));
}
view raw Stepdefs.java hosted with ❤ by GitHub
And an example of it being down:

@Given("^The company wide user repository is unavailable$")
public void userServiceDown() throws Throwable {
givenThat(get(urlEqualTo("/user/" + id)).willReturn(
aResponse().withStatus(500)
));
}
view raw Stepdefs.java hosted with ❤ by GitHub
So the next requirement is that Cassandra being down doesn't make your service fail i.e.

Scenario: Company wide information store is up but data store down
Given The data store is down
When User information is requested
Then The user information is returned
Scenario: Data store being slow should not cause slow down user transactions
Given The data store has problems writing
When User information is requested
Then The user information is returned
The first one you could stop Cassandra, perhaps using the great tool CCM. However this is slow, and you need to write code to make sure it is back up/down, all of this in a different process. And how about the next test? How do we make Cassandra return the result slowly? Or produce a Write Timeout Exception?

That is where Stubbed Cassandra comes in handy. To get is started you can add some code like this to start it before the tests and close it after tests:

private final static Scassandra SCASSANDRA = ScassandraFactory.createServer();
...
@Before
public void beforeScenario() {
SCASSANDRA.start();
}
...
@After
public void after() {
SCASSANDRA.stop();
}
view raw Stepdefs.java hosted with ❤ by GitHub
Now implementing the step definition to mimic Cassandra being down is as easy as:

@Given("^The data store is down$")
public void cassandraDown() throws Throwable {
SCASSANDRA.stop();
}
view raw Stepdefs.java hosted with ❤ by GitHub
Which is a lot quicker than turning off a real Cassandra, and starting it back up in the @Before is also very quick.

Now to mimic Write time outs in Cassandra:

@Given("^The data store has problems writing$")
public void cassandraWriteTimeouts() throws Throwable {
PrimingRequest existingUser = PrimingRequest.queryBuilder()
.withQuery("insert into users(id, firstName, lastName) values ('chbatey','Chris','Batey')")
.withResult(PrimingRequest.Result.write_request_timeout)
.build();
SCASSANDRA.primingClient().primeQuery(existingUser);
}
view raw Stepdefs.java hosted with ❤ by GitHub
Very similar things could be done for read timeouts and unavailable exceptions.

This article gave you an insight to how you can behaviour drive features relating to Cassandra being down. The full code and running tests are here. Full information on how to use Stubbed Cassandra is here, you'll probably want to documentation for the Java client for Scassandra which is here.


No comments: