Click here to Skip to main content
Click here to Skip to main content

User Story is Worthless, Behavior is What We Need

, 11 Jun 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
User Story is Worthless, Behavior is What We Need

Introduction

User Story is suitable for describing what user needs, but not what user does and how system reacts to user actions within different contexts. It basically gives product team a way to quantify their output and let their boss know that they are doing their job. As a developer, you can't write code from user stories because you have no clue on what is the sequence of user actions and system reactions, what are the validations, what APIs to call and so on. As a QA, you can't test the software from user stories because it does not capture the context, the sequence of events, all possible system reactions. User stories add little value to dev lifecycle. It only helps product team understand how much work they have to do eventually and it helps finance team get a view on how much money people are talking about. But to UI designers, solution designers, developers, they are nothing but blobs of highly imprecise statements that leave room for hundreds of questions to be answered. The absence of “Context” and “Cause and Effect”, and the imprecise way of saying “As a...I want... so that...” leaves room for so many misinterpretations that there’s no way development team can produce software from just user stories without spending significant time all over again analysing the user stories. Software, and the universe eventually, is all about Cause and Effect. The Cause and Effect is not described in a user story.

Unlike user stories, the “Behavior” suggested by Behavior Driven Development (BDD) is a much better approach because the format of a behavior (Given context, When event, Then outcome), when used correctly, lets you think in terms of sequence of events, where the context, event and outcome are captured for each and every action user or system does, and thus works as a definite spec for designing the UI and architecture. It follows the Cause and Effect model, thus it can explain how the world (or your software) works. It can be so precise that sometimes a behavior works as a guideline for a developer to write a single function! Not just the developers, even the QA team can clearly capture what action they need to perform and how the system should respond. However, to get the real fruit out of behaviors, you need to write them properly, following the right format. So, let me give you some examples on how you can write good behaviors for UI, business layer, services and even functions and thus eliminate repeated requirement analysis that usually happens throughout the user-story driven development lifecycle.

If you can force product managers to be 10% more precise in defining the requirements before passing it down the life-cycle, then you can save almost 30% of the total waste cost throughout the development life-cycle by saving time and cost in post analysis discussions, documentation and revisions.

User Stories to Behaviors

If you still live in the imperfect world where you get user stories dumped on your desk and you have to produce code from it, you need to learn to convert user stories into behaviors. Here’s an example:

As an anonymous user, I want to login on the homepage, 
so that I can access my account details. 

The above user story leaves the door for many interpretations. Give the above user story to a developer and then see how many round trips it takes between developer and product team and eventually between developer and QA to get this shipped to production. How does user login on the homepage? By going to a login page or the login box is embedded on the homepage? Is username in email address format or free form text? What do we show when the username is wrong or password is wrong?

So, you have to back this user story with acceptance criteria that will answer these questions:

Acceptance Criteria

  • Homepage must show a login box
  • System must validate username and password
  • Username must be in email address format
  • Password must be 6 characters and 1 numeric
  • System must show the dashboard when username and password is correct
  • System must show error if the username and password is entered wrong

First, you need to write some acceptance criteria to describe the environment user is in. You have to describe there’s a login box, it has username and password. Then you have to describe some user actions. How does user enter the username and password. Then you have to describe the system’s behavior how it reacts. All these are put as acceptance criteria as Assertive Statements that must result in True/False. This is a ridiculous way of describing user actions and system reaction to those actions. Software (and the universe) is all about Cause and Effect. This Cause and Effect is totally missing from user story.

If you convert this user story to behavior, it will look like this:

Given an anonymous user who has registered before and is on the homepage login box, 
When user enters username and password on the login box and 
clicks Login button or hits enter, 
Then it should validate the username and password and 
redirect user to the dashboard if the account is valid, 
and it should show invalid username and password inside the login box 
when the credentials are incorrect, 
and it should update the last login datetime for the user in database. 

Here you see the context (e.g., anonymous user, on homepage) is clearly separated and explained in the “Given” block, unlike user stories where the context is spread in some random order in acceptance criteria. Then the user actions are clearly explained in the “When” block, unlike user stories where the user’s action is partly described in “I want to...” part and then partially described in some of the acceptance criteria that you need to figure out yourself. The outcome, both positive and negatives are described in “Then” block in the behavior, which is equivalent to user story’s “So that...” and accompanying acceptance criteria. As you see, in one behavior statement, you can precisely define the environment or context user is in before performing the action, then user performing the action and then the system reactions to the action. The past, the present and the future sequence is clearly followed in a behavior.

Behaviors for the UI

Behaviors is a great way to describe UI requirements since it clearly captures the sequence of user actions and all possible system reactions. For example:

Given an account holder with some outstanding payments and direct debits setup, 
When user goes to the Account Details page by clicking the link on the header area, 
Then it should show the users’ account numbers and balances in a grid view, 
and it should show balance in red if it’s a negative balance, 
and it should show a list of outstanding payments in red, 
and it should show a list of direct debits setup in a grid.

Give this behavior to a developer and the developer will scream with joy and produce beautiful code out of this. Compared to this behavior if you give the equivalent user story:

As an account holder, I want to go to Account Details page 
so that I can see my accounts and their balances, 
my outstanding payments and direct debits.

Acceptance Criteria

  • The account holder already has some outstanding payments.
  • The account holder already has some direct debits configured.
  • A grid view shows the account numbers and balances.
  • Negative balance must be in red.
  • A list shows outstanding payments in red.
  • A grid shows direct debits, if any.

It takes a lot more sentences to describe the same behavior. Moreover, the first two acceptance criteria, which describes the context, aren't really acceptance criteria. Since there’s no other way to put context in the user story, it has to be put somewhere on the acceptance criteria. Finally, there’s no sequence of events happening. It does not say how user goes to the accounts details page.

I use behavior to describe the UI requirements in my open source project Dropthings. It’s a Web 2.0 start page that renders widgets and allows you to customize the widgets. I can define the first user visit behavior in this way:

Given a new user, 
When user visits the homepage, 
Then it should show the default widgets.

I can then use this exact same behavior to write test code that automates browser, simulates the user actions and verifies the expectations.

[Specification]
public void Visit_Homepage_as_new_user_and_verify_default_widgets()
{
    var browser = default(Browser);

    "Given a new user".Context(() =>
        {
            BrowserHelper.ClearCookies();
        });
    "When user visits the homepage".Do(() =>
        {
            browser = BrowserHelper.OpenNewBrowser(Urls.Homepage);
        });
    "It should show the default widgets".Assert(() =>
        {
            using (browser)
            {
                var expectedWidgets = new string[] 
                {
                    "How to of the Day",
                    "Omar's Blog (Fast RSS)",
                    "Book on building Dropthings",
                    "Twitter",
                    "Fast Flickr",
                    "Digg - Silverlight Widget",
                    "BBC World",
                    "Weather",
                    "CNN.com",
                    "Travelocity",
                    "Stock",
                    "HTML"
                };

                var homepage = browser.Page<HomePage>();

                Assert.Equal(expectedWidgets.Length, homepage.Widgets.Count);

                Assert.True(
                    expectedWidgets.All(
                        widgetTitle =>
                            homepage.Widgets.Any(
                                control => string.Compare(control.Title,
						widgetTitle, true) == 0)));
            }
        });
}

The above test is written using xUnit, Subspec (xUnit extension) and WatiN. You can find hundreds of such tests in the project that shows you how you can use behavior to define the UI and then write code that automates browser, simulates real user actions and checks the output on the browser to find out if the behavior is satisfied. Dropthings is an AJAX web site. The test codes are rich in AJAX tricks and tips and should help you understand how to automated UI testing.

Behaviors for Services

You can use the behavior format to clearly define what input and output you expect from a service. For example:

Given an account holder with some outstanding payments and direct debit setup, 
When GetAccountDetails method of AccountService is called with the account holder’s ID 
Then it should return the account summary containing account names and balances, 
and it should return an array of overdue payments, 
and it should return an array of direct debits.

Now if you had to define this as a user story, oh boy:

As the Account Details page, I want to call GetAccountDetails of AccountService so that 
I can get the account summary, overdue payments and direct debits.

In fact, it’s totally wrong. There’s no “user” here that will use the user story format. You have no choice but to use some other approach to convey such requirements to distributed component development teams. And the best approach is to use such behaviors to describe the behavior of the components.

Behaviors for Business Components

Just like services, you can describe behavior for Business Components. For example:

Given a user who has accounts with the bank, 
When LoginManager.Login is called with user’s username and password, 
Then it should return true if the username and password is valid 
by calling Active Directory API, 
and it should update the last login date time of the user in Audit database, 
and it should throw exception if the username and password is wrong.

If you hand over this behavior to a developer, the developer can write the business components in his/her sleep. It does not need further discussion. It does not require some UML diagram that shows who’s calling who. It’s a super fast way to get requirements communicated to the developers. Moreover, if you are writing unit tests, then you can use this exact behavior to produce unit tests.

Here’s an example of creating a business layer component using a behavior:

[Specification]
public void First_visit_should_create_same_pages_and_widgets_as_the_template_user()
{
    var profile = default(UserProfile);
    UserSetup userVisitModel = null;
    var facade = default(Facade);
    var anonUserName = default(string);
    var anonTabs = default(List<Tab>);
           
    "Given anonymous user who has never visited the site before".Context(() => 
    {
        profile = MembershipHelper.CreateNewAnonUser();
        facade = new Facade(new AppContext(string.Empty, profile.UserName));

        // Load the anonymous user pages and widgets
        anonUserName = facade.GetUserSettingTemplate().AnonUserSettingTemplate.UserName;
        anonTabs = facade.GetTabsOfUser(facade.GetUserGuidFromUserName(anonUserName));

    });

    "When the user visits for the first time".Do(() =>
    {                
        userVisitModel = facade.FirstVisitHomeTab
		(profile.UserName, string.Empty, true, false);
    });

    "It creates widgets on the newly created page at exact columns 
	and positions as the anon user's pages".Assert(() =>
    {
        anonTabs.Each(anonTab =>
        {
            var userTab = userVisitModel.UserTabs.First(page =>
                            page.Title == anonTab.Title
                            && page.OrderNo == anonTab.OrderNo
                            && page.PageType == anonTab.PageType);

            facade.GetColumnsInTab(anonTab.ID).Each(anonColumn =>
            {
                var userColumns = facade.GetColumnsInTab(userTab.ID);
                var userColumn = userColumns.First(column =>
                                column.ColumnNo == anonColumn.ColumnNo);

                var anonColumnWidgets = 
		facade.GetWidgetInstancesInZoneWithWidget(anonColumn.WidgetZone.ID);
                var userColumnWidgets = 
		facade.GetWidgetInstancesInZoneWithWidget(userColumn.WidgetZone.ID);

                // Ensure the widgets from the anonymous user template's columns are 
                // in the same column and row.
                anonColumnWidgets.Each(anonWidget => 
		Assert.True(userColumnWidgets.Where(userWidget =>
                        userWidget.Title == anonWidget.Title
                        && userWidget.Expanded == anonWidget.Expanded
                        && userWidget.State == anonWidget.State
                        && userWidget.Resized == anonWidget.Resized
                        && userWidget.Height == anonWidget.Height
                        && userWidget.OrderNo == anonWidget.OrderNo).Count() == 1));
            });
        });

        facade.Dispose();
    });

}

The above code might look like a lot of code, but the point I am trying to make is you can produce business layer components out of behavior and you can test them using the same behavior.

Behaviors for Data Access Layer

You can even define behaviors for your database just like the way you do for services and business layer. If you have some SP that does some complex job, you can use the behavior format to define what the SP should do. For example:

Given a user in the aspnet_users table, 
When AuthenticateUser is SP is called with the user’s loweredusername and password,
Then it should query the aspnet_users table to find a match,
and it should compare the password with the selected row in a case sensitive way,
and it should return the aspnet_user row if the username and 
password matches successfully,
and it should return nothing if there’s no match.

Once you define the behavior this way, you can write the SP from it and then you can use some unit test tool to test the SP.

Behaviors for Functions!

If you have a function that does more than a trivial job, then you should use a behavior to explain what the function should do. For example:

Given a file in a local folder
When File.ReadAllLines is called with the file’s full path,
Then it should open the file and read all the content and return a string array,
and it should throw InvalidArgumentException if the path is wrong,
and it should return a null array if the file is zero length file,
and it should throw InvalidArgumentException if the path is a UNC or URL,

The above behavior is invaluable for writing unit tests. Once you get the function coded as per the behavior, then you can easily write a unit test for the function.

Again, here’s an example how I write behavior to explain what a particular function should do:

[Specification]
public void GetTab_Should_Return_A_Tab_from_cache_when_it_is_already_cached()
{
    var cache = new Mock<ICache>();
    var database = new Mock<IDatabase>();
    ITabRepository pageRepository = new TabRepository(database.Object, cache.Object);

    const int pageId = 1;
    var page = default(Tab);
    var sampleTab = new Tab() { ID = pageId, 
	Title = "Test Tab", ColumnCount = 3, LayoutType = 3, VersionNo = 1, 
        	PageType = (int)Enumerations.PageType.PersonalTab, CreatedDate = DateTime.Now };

    "Given TabRepository and the requested page in cache".Context(() =>
    {
        cache.Expect(c => c.Get(CacheKeys.TabKeys.TabId(sampleTab.ID)))
                .Returns(sampleTab).AtMostOnce();
    });

    "when GetTabById is called".Do(() =>
            page = pageRepository.GetTabById(1));

    "it checks in the cache first and finds the object is in cache".Assert(() =>
    {
        cache.VerifyAll();
    });

    "it returns the page as expected".Assert(() =>
    {
        Assert.Equal<int>(pageId, page.ID);
    });
}

Here the behavior is explaining what GetTab function of TabRepository should do. I can then produce a unit test and use mocking frameworks like Moq to write a unit test for the function.

Conclusion

Behavior is an all-rounder solution to specify requirements for UI, services, business component, database, utility libraries and even for complex functions. The format encourages the requirement to be described in a precise way and leaves little room for confusion, when followed properly. Compared to user stories which can only explain user intentions, if behavior is used throughout the development lifecycle, it can greatly reduce repeated requirement analysis effort and can make the communication between product, design, development and QA team much more effective. If you can force product owners to be 10% more precise in defining the requirements, then you can save almost 30% of the total waste cost throughout the development life-cycle by saving time and cost in post analysis discussions, documentation and revisions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Omar Al Zabir
Architect BT, UK (ex British Telecom)
United Kingdom United Kingdom

Comments and Discussions

 
GeneralMy vote of 3 Pinmemberke4vtw2-Aug-10 8:46 
>>you get user stories dumped on your desk and you have to produce code from it
 
If this is what is happening, then your systems analysts are not doing their jobs!
 
I submit that you need your user story before you can come up with your requirements. What you are describing is a step that certainly needs to take place between the user story and the creation of developer tasks.
 
Many product owners are not technical people; they are business-oriented people who know WHAT needs to happen, but not HOW it needs to happen. The "how"... your requirements, need to be worked out by a qualified systems analyst/architect with input from the product owner to develop specific tasks that are appropriate to hand to the developers.
 
While I like your approach to this phase of development, I don't understand or agree with your hostility to user stories. They are not technical specs, and should not be taken as such, but they do serve to start the appropriate conversations.
GeneralRe: My vote of 3 PinmemberLadislav Mrnka2-Aug-10 23:36 
GeneralRe: My vote of 3 PinmvpOmar Al Zabir2-Aug-10 23:47 
GeneralRe: My vote of 3 PinmemberLadislav Mrnka3-Aug-10 0:28 
GeneralRe: My vote of 3 PinmvpOmar Al Zabir3-Aug-10 0:34 
GeneralRe: My vote of 3 PinmemberLadislav Mrnka3-Aug-10 1:01 
GeneralRe: My vote of 3 PinmvpOmar Al Zabir3-Aug-10 1:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141220.1 | Last Updated 11 Jun 2011
Article Copyright 2010 by Omar Al Zabir
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid