Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C#

User Story is Worthless, Behavior is What We Need

Rate me:
Please Sign up or sign in to vote.
3.17/5 (16 votes)
11 Jun 2011CPOL9 min read 138K   32   38
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.

C#
[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:

C#
[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:

C#
[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)


Written By
Architect BT, UK (ex British Telecom)
United Kingdom United Kingdom

Comments and Discussions

 
QuestionStatistical data? Pin
Jerry D'Antonio16-Mar-12 3:02
Jerry D'Antonio16-Mar-12 3:02 
GeneralMy vote of 1 Pin
OnAClearDiskYouCanSeekForever13-Jun-11 18:03
OnAClearDiskYouCanSeekForever13-Jun-11 18:03 
General[My vote of 2] Reasonable Article [modified] Pin
Member 456543312-Jun-11 1:38
Member 456543312-Jun-11 1:38 
General[My vote of 1] This article demonstrates a misunderstanding of both user stories and acceptance criteria Pin
Tabdee21-Mar-11 4:58
Tabdee21-Mar-11 4:58 
AnswerRe: [My vote of 1] This article demonstrates a misunderstanding of both user stories and acceptance criteria Pin
Omar Al Zabir21-Mar-11 5:39
Omar Al Zabir21-Mar-11 5:39 
GeneralRe: [My vote of 1] This article demonstrates a misunderstanding of both user stories and acceptance criteria Pin
Tabdee21-Mar-11 8:57
Tabdee21-Mar-11 8:57 
GeneralRe: [My vote of 1] This article demonstrates a misunderstanding of both user stories and acceptance criteria Pin
Omar Al Zabir22-Mar-11 1:02
Omar Al Zabir22-Mar-11 1:02 
GeneralRe: [My vote of 1] This article demonstrates a misunderstanding of both user stories and acceptance criteria Pin
Tabdee22-Mar-11 2:06
Tabdee22-Mar-11 2:06 
GeneralRe: [My vote of 1] This article demonstrates a misunderstanding of both user stories and acceptance criteria Pin
OnAClearDiskYouCanSeekForever13-Jun-11 18:14
OnAClearDiskYouCanSeekForever13-Jun-11 18:14 
GeneralRe: [My vote of 1] This article demonstrates a misunderstanding of both user stories and acceptance criteria Pin
Omar Al Zabir24-Jun-11 12:51
Omar Al Zabir24-Jun-11 12:51 
GeneralMy vote of 4 Pin
John Brett10-Nov-10 0:40
John Brett10-Nov-10 0:40 
GeneralMy vote of 3 Pin
kiquenet.com30-Aug-10 2:45
professionalkiquenet.com30-Aug-10 2:45 
GeneralMy vote of 2 Pin
Christian Rodemeyer12-Aug-10 9:50
professionalChristian Rodemeyer12-Aug-10 9:50 
GeneralScrum is a form of revenge from PM's and BA's to Developers Pin
Cephus6-Aug-10 12:10
Cephus6-Aug-10 12:10 
GeneralRe: Scrum is a form of revenge from PM's and BA's to Developers Pin
Vara3224-Oct-16 3:06
Vara3224-Oct-16 3:06 
GeneralMy vote of 5 Pin
Cephus6-Aug-10 12:08
Cephus6-Aug-10 12:08 
GeneralMy vote of 3 (3.5 if I could) Pin
Kelly Summerlin4-Aug-10 6:23
Kelly Summerlin4-Aug-10 6:23 
GeneralUser stories are not behaviors Pin
Petr Kozelek2-Aug-10 22:40
Petr Kozelek2-Aug-10 22:40 
GeneralRe: User stories are not behaviors Pin
Omar Al Zabir2-Aug-10 22:51
Omar Al Zabir2-Aug-10 22:51 
GeneralRe: User stories are not behaviors Pin
Petr Kozelek3-Aug-10 0:15
Petr Kozelek3-Aug-10 0:15 
GeneralRe: User stories are not behaviors Pin
Omar Al Zabir3-Aug-10 0:31
Omar Al Zabir3-Aug-10 0:31 
GeneralNice article, but a small remark about the user stories' details Pin
Tom Janssens2-Aug-10 20:01
Tom Janssens2-Aug-10 20:01 
You give the following example :

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.


But, in order to do it correct, IMHO you should actually convert this into multiple Scenarios, i.e.

Story Anonymous user logs in

As an anonymous user
I should be able to login
So I can see my history and other specifics for this site...

Given the current user is an anonymous user
and the current page is the homepage

Scenario the user logs in with a valid account and a valid password
Given the user enters a valid username
and the user enters a valid password
When the user logs in
Then ....

Scenario the user logs in with an invalid account

Scenario the user logs in with a valid account and an invalid password


etc...

I also agree on the ui design comment; in my opinion prototype ui design and Specs should be created together...
(FYI, I also published an article on this on my blog[^])

GeneralRe: Nice article, but a small remark about the user stories' details Pin
Omar Al Zabir2-Aug-10 22:14
Omar Al Zabir2-Aug-10 22:14 
GeneralGreat article, just one or two suggestions and points of view Pin
RayLouw2-Aug-10 10:25
RayLouw2-Aug-10 10:25 
GeneralRe: Great article, just one or two suggestions and points of view Pin
Omar Al Zabir2-Aug-10 23:30
Omar Al Zabir2-Aug-10 23:30 

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

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