65.9K
CodeProject 正在变化。 阅读更多。
Home

“PhraseFixture”的优点——比用于 FIT 和 FitNesse 验收测试框架的 DoFixture() 更好

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2011年8月14日

CPOL

3分钟阅读

viewsIcon

17992

FitLibrary 中强大的 DoFixture() 模拟了类英文的规范。PhraseFixture 提案将其提升到了一个新的水平。

“PhraseFixture” 提案

背景

本文假定您已了解 FIT (Framework for Integrated Test) 和 FitLibrary

概述

对于那些使用过 FitLibraryDoFixture 的读者来说,您可能熟悉 ChatStart 示例(FitLibraryUserGuide.DoFixture.WritingFixtures)。如下所示:

  • 第一个表格的 fixture 是一个 DoFixture,因此创建的流程 fixture 对象会处理其余的表格。

ChatStart
  • 第二个表格包含一个动作,该动作被映射到(初始)流程 fixture 对象connectUser() 方法,如下所示:
connect user sarah
  • 第三个表格包含两个动作,它们也应用于流程 fixture 对象
用户 sarah creates fit Room
用户 sarah enters fit Room

DoFixture 试图做的是让表格数据更具可读性。例如,在上面的表格中,最后一行试图尽可能地接近人们在英语中会说的话:“User Sarah enters fit room”。这里有两个问题:

  1. 短语被拆分到表格的多个列中,这似乎很奇怪。当大多数人看到表格时,他们通常会想到一个网格,其中行是“记录”,每一列代表某种字段。对于 FIT 中的 TableFixture 来说,表格是一个很好的方法。但是 DoFixture 将表格的隐喻推向了极限。当人们创建表格,其中每一行根据“参数”的数量具有不同的列数时,情况会变得更糟。
  2. 另一个问题是,即使忽略了短语在表格中的事实,“User Sarah enters fit room”听起来也很奇怪。难道不应该交换“fit”和“room”吗?好吧,这样听起来会更好,但对于 DoFixture 来说却行不通。这是因为 DoFixture 依赖于命令和参数在列之间交替。在上面包含“sarah”和“fit”这几个词的五列中,它们构成了参数,而“user”、“enters”和“room”这几个词被连接起来以对应 fixture 方法 userEntersRoom();。请参阅下面的类:
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);
    }
}

提案:PhraseFixture()

有可能让 fixture 变得足够智能,即使短语中的单词不遵循列交替顺序也能找到正确的方法。此外,如果使用空格作为分隔符,那么行看起来会好得多。例如:

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

这肯定比

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

看起来更好,并且可能对业务分析师来说更容易输入到表格中。

实现这一点的方法很简单,就是尝试单词的组合,直到找到匹配的方法,而未使用的单词则成为参数。所以对于第四行,“User”、“Enters”和“Room”这几个词被匹配到 UserCreatesRoom() 方法,未使用的单词“Sarah”和“fit”成为方法的参数。

缺点

每个解决方案都有其自身的问题。虽然这比 DoFixture 读起来更好,但由于使用空格作为单词分隔符,包含空格的 string 参数将需要以其他方式处理。提案要求将 string 放在引号中。因此,例如,如果上面的用户名是“sarah connor”而不是“sarah”,那么作者就必须输入:

User “sarah connor” enters room fit

这还不错。

算法

该算法非常简单,也许可以改进。但它的工作原理是这样的。举一个简单的例子,假设短语中的单词是“Connect user sarah”。它会查找以下方法名称,并且顺序确保首先查找最长可能的方法名称:

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

(加粗的那个显然是会被找到的,因为该方法存在于 fixture 中。并且由于该特定匹配没有使用“Sarah”这个词,所以这个词将成为 ConnectUser() 函数的参数。)

我相信可以想出更智能的算法,但这里包含的算法足以证明可行性。

支持代码

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)

© . All rights reserved.