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

The Case for “PhraseFixture” -- A Better DoFixture() for Use with FIT and FitNesse Acceptance Testing Framework

, 13 Aug 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
The powerful DoFixture() in FitLibrary simulates English like specification. The PhraseFixture proposal takes this to the next level.

The Case for “PhraseFixture”

Background

This articles assumes knowledge of FIT (Framework for Integrated Test) and the FitLibrary.

Overview

For those who’ve used DoFixture from FitLibrary, you are probably familiar with the ChatStart example (FitLibraryUserGuide.DoFixture.WritingFixtures). Shown here:

  • The fixture of the first table is a DoFixture, so the created flow fixture object handles the rest of the tables:

ChatStart
  • The second table contains an action, which is mapped into the method connectUser() of the (initial) flow fixture object, as shown below:
connect user sarah
  • The third table contains two actions which are also applied to the flow fixture object.
user sarah creates fit Room
user sarah enters fit Room

What DoFixture attempts to do is to make the table data be more readable. For instance, in the above table, the last row is attempting to be as close as possible to what one would say in English: “User Sarah enters fit room”. There are two problems here:

  1. The phrase is split across multiple columns of a table, which seems odd. When most people see a table, they typically think of a grid, where the rows are “records”, each column represents some kind of a field. For a TableFixture in FIT, a table is a good method. But DoFixture pushes the table metaphor over its limit. Things get worse when people create tables where each row has different number of columns, based on the number of “parameters”.
  2. The other problem with this is that, even ignoring the fact that the phrase is in a table, reading “User Sarah enters fit room” sounds weird. Shouldn’t “fit” and “room” be swapped? Well, it would sound better, but it wouldn’t work with DoFixture. This is because DoFixture relies upon commands and parameters alternating from one column to the next. In the above five columns containing the words “sarah” and “fit” constitute the parameters, and the words “user”, “enters” and “room” are concatenated to correspond to the fixture method userEntersRoom();. See the class below:
public class ChatStart : DoFixture
{
    private ChatRoom chat = new ChatRoom();

    public ChatStart()
    {
        setSystemUnderTest(chat);
    }
    
    public bool ConnectUser(string userName)
    {
        return chat.connectUser(userName);
    }

    public bool UserCreatesRoom(string userName, string roomName)
    {
        return chat.userCreatesRoom(userName, roomName);
    }
    
    public bool UserEntersRoom(string userName, string roomName)
    {
        return chat.userEntersRoom(userName, roomName);
    }
}

Proposal: PhraseFixture()

It’s possible to have a fixture be smart enough to locate the right method even if the words in the phrase did not follow the column alternating sequence. Furthermore, if spaces were used as the delimiter, then the rows would look a lot better. Example:

Chat Start
Connect user sarah
User sarah creates room fit
User sarah enters room fit

Which certainly looks better than:

ChatStart
connect user sarah
user sarah creates fit room
user sarah enters fit room

… and is probably easier for the business analyst to type into a table.

The way this can be made to work is simply by trying the combinations of words until a matching method is found, and the unused words become the parameters. So for the fourth row, the words “User”, “Enters” and “Room” get matched to the method UserCreatesRoom(), and the unused words, “Sarah” and “fit” end as the parameters to the method.

Downsides

Every solution has its set of problems. Although this reads better than DoFixture, since spaces are used as word delimiters, string parameters that contain spaces will have to be dealt with another way. The proposal is to require to place strings in quotes. So, for instance, if the user name above was “sarah connor” instead of “sarah”, then the author would have to have entered:

User “sarah connor” enters room fit

which isn’t so bad.

Algorithm

The algorithm is quite simple, and perhaps can be improved. But what it does is like this. As a simpler example, let’s say that the words in the phrase are “Connect user sarah”. It will look for the following method names, and the order ensures that the longest possible name is looked for first:

  • ConnectUserSarah()
  • UserSarah()
  • ConnectSarah()
  • Sarah()
  • ConnectUser()
  • User()
  • Connect()

(The one in bold is obviously the one that will be found since that method exists in the fixture. And since the word “Sarah” is not used in that particular match, that word becomes the parameter to the ConnectUser() function.)

I’m sure it’s possible to come up with a smarter algorithm, but the one included here serves the purpose of demonstrating what is possible.

Supporting Code

const int cBitsInInt = 32;

static uint ReverseBits(uint x)
{
    uint h = 0;
    uint i = 0;
    
    for (h = i = 0; i < cBitsInInt; i++)
    {
        h = (h << 1) + (x & 1); 
        x >>= 1; 
    }
    
    return (uint)h;
}

static uint ReverseAndShift(uint x, int length)
{
    return ReverseBits(x) >> (cBitsInInt - length);
}

static bool FindMethod(MethodInfo[] methods, string[] columns, 
	out MethodInfo foundMethod, out List<string> parameters)
{
    uint bits = (uint)(1 << columns.Length);
    for (uint i = bits - 1; i >= 0 && i <= cBitsInInt; i--)
    {
        string s = "";
        parameters = new List<string>();
        
        uint x = ReverseAndShift(i, columns.Length);
        for (int j = 0; j < columns.Length; j++)
        {
            if ((1 & x) > 0)
                s += columns[j].ToLowerInvariant();
            else
                parameters.Add(columns[j]);
                
            x >>= 1;
        }
        
        foreach (MethodInfo method in methods)
        {
            if (method.Name.ToLowerInvariant() == s)
            {
                foundMethod = method;
                return true;
            }
        }
    }
    
    foundMethod = null;
    parameters = null;
    return false;
}

Kenneth Kasajian (ken@kasajian.com)

License

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

Share

About the Author

Kenneth Kasajian

United States United States
My interests mostly revolve around making machines do work for people. I'm a computer programmer, software architect, development manager, program manager and a computer programmer. I said programmer twice because I believe you need to be able to do the work yourself if you're going to direct others. I started my career creating software for abstract art, followed by work in embedded systems and HMI. In the 90s I created a successful product called Visual DLL and helped develop the Sales Force Automation product, Arsenal. I've often been involved in online communities, creating games, utilities, and collaboration software. I'm passionate about agile requirements management, acceptance testing through executable specification, and anything that will make developers more productive. My current role is Principal Scientist where I get to work on different technologies with an awesome team, to solve real-world practical problems. I'm Armenian, so talking is in my nature -- if you see me online or offline, say hi and we'll geek out about the latest tools and libraries. If you learned something cool recently (and you should if you're a lifelong learner), then I'd like to hear about it.
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.141015.1 | Last Updated 14 Aug 2011
Article Copyright 2011 by Kenneth Kasajian
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid