Automated testing with FubuMVC, StoryTeller, and Serenity
In my previous post I talked about some foundational ideas for automated testing. Rather than continuing on with conceptual points, let’s take a look at an example of how we do automated testing in the world of Fubu.
While some of the tools here are Fubu-specific, the approaches and StoryTeller are NOT Fubu-specific.
In most automated testing scenarios, you find yourself quickly bumping into issues like:
- Test data bleeding between tests (*ahem* shared databases)
- Too many unknown data dependencies (hardcoded usernames, identifiers)
The idea of “System State” is simply providing a declarative way for establishing the state of your system for *each* test.
One trick that my team has used in the past is changing our underlying persistence strategy from database to in-memory for automated testing. Some databases (i.e., RavenDB) provide a way for you to do this. We used a basic abstraction called FubuPersistence to do this**. This means that we simply modify our IoC container for these scenarios.
As I mentioned, “System State” is all about declaratively specifying the state of your system for *each* test. In StoryTeller world, we do this via a Fixture.
If you’re unfamiliar with StoryTeller, then this will serve as a crash course. Let’s take a look at the following fixture:
Turn your attention to line 35. This method allows us to create a test in StoryTeller like this:
Example 1: Verify that the Subscription Plans are displayed
One of the examples from my last post presented a test with the following steps:
- The available subscriptions are: “Subscription 1, Subscription 3”
- Navigate to the Registration screen
- The displayed subscriptions are: “Subscription 1, Subscription 3”
Using our SystemStateFixture, let’s go ahead and define the state:
Using the FubuMVC helpers in Serenity, the registration fixture is nice and simple:
Notice that before running the fixture, we instruct the test to navigate to the Registration screen. More notably, we are using our endpoint/actions to define the route. No magic strings here.
We’re reusing the html conventions from our FubuMVC application by retrieving the select element: Driver.InputFor
Using our VerifySubscriptionPlans grammar, we can then define the following step:
Example 2: After registering for an account, a confirmation email should be received
One of the benefits to how the combination of FubuMVC, FubuMVC.SelfHost, Serenity, and StoryTeller works is that everything is in a single AppDomain. How is that beneficial, you might ask?
Let’s take a look at our email gateway class again:
We have a helper class here called SmtpServerController that wraps our usage of EmbeddedMail:
We’re injecting our EmailSettings class (under the assumption that the settings need to vary). Because we’re operating under the same AppDomain, this means that we can configure our container and have it affect the behavior of our FubuMVC application.
Let’s turn attention to the StubEmailGateway method:
We start the server and the configure an instance of EmailSettings inside of our IoC container. Now when we make any calls it IEmailGateway.Send, we will be sending messages to our embedded SMTP server.
Let’s define our system state:
And now we’ll use our VerifyEmail grammar:
It’s a lot of information, I know. You’re better off pulling down the code and running through it yourself.
Simply clone the repo, run rake to be sure you can compile, and then rake run_st. This will launch StoryTeller for you and you can browse for the project file located under src/SystemStateExample.StoryTeller/example.xml.