BDD测试中的Given是否可以工作或仅验证状态?

时间:2011-03-02 03:35:04

标签: bdd

BDD场景中的Given语句是否有效或仅验证状态?

例如,请考虑此示例:“鉴于我已登录”

步骤定义是否应该记录用户,还是应该仅验证我是否已登录?

如果它只应该验证我已登录,那么我该如何实际登录?

2 个答案:

答案 0 :(得分:1)

答案取决于您要测试的内容。这个例子听起来像黄瓜的东西,但你用的是BDD这个词。我将BDD与自下而上的单元测试联系起来。如果是这种情况,我们假设您拥有某种类型的身份验证类和用户类。

用户类:

public class User : IUser
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Password { get; set; }
}

public interface IUser
{
    int ID { get; }
    string Name { get; }
    string Password { get; }
}

Authenticator Class:

    public class Authenticator : IAuthenticator
    {
        public Authenticator()
        {
            //Initialize and acquire some authentication mechanism (db, service, file, etc..)
        }

        public bool IsLoggedIn(IUser user)
        {
            return true;
        }
        public bool LoginUser(IUser user)
        {
            //Do real login.
            return true;
        }

        public bool LogoutUser(IUser user)
        {
            //Do real logout.
            return true;
        }
    }

    public interface IAuthenticator
    {
        bool LoginUser(IUser user);
        bool LogoutUser(IUser user);
    }

用于调用身份验证器,管理权限等的UserActions类:

public class UserActions
{
    private readonly IAuthenticator _authenticator;
    private readonly IUser _user;
    //private Permissions Permissions;

    public UserActions(IUser user)
        : this(new Authenticator(), user)
    { }

    public UserActions(IAuthenticator authenticator, IUser user)
    {
        _authenticator = authenticator;
        _user = user;
    }

    public bool Login()
    {
        var result = _authenticator.LoginUser(_user);
        return result;
    }

    public void Logout()
    {
        _authenticator.LogoutUser(_user);
    }
}

使用这些类,我将尝试测试UserActions类在登录和注销方面的行为(使用NUnit和MOQ)。在这个例子中,没有做任何工作,但为了举例,想象一个更全面的实现:

[TestFixture]
public class Tester
{
    [Test]
    public void CanLoginGoodUser()
    {
        IUser user = new User { ID = 1, Name = "Test1", Password = "good" };
        var authenticator = new Mock<IAuthenticator>();
        authenticator.Setup(a => a.LoginUser(user)).Returns(true);

        var action = new UserActions(authenticator.Object, user);
        action.Login();
        authenticator.Verify(a => a.LoginUser(It.Is<IUser>(u => u == user)), Times.AtLeastOnce());
    }

    [Test]
    public void WontLoginBadUser()
    {
        IUser user = new User { ID = 1, Name = "Test1", Password = "bad" };
        var authenticator = new Mock<IAuthenticator>();
        authenticator.Setup(a => a.LoginUser(user)).Returns(false);

        var action = new UserActions(authenticator.Object, user);
        action.Login();
        authenticator.Verify(a => a.LoginUser(It.Is<IUser>(u => u == user)), Times.AtLeastOnce());
    }
}

我实际上没有登录或退出。我所做的是利用接口提供预设响应,该响应显示类在与其消耗的类交互时的行为方式。在这种情况下,它是身份验证器和用户对象。请注意,我没有模拟用户类,因为此时没有必要。

我相信其他人会不同意这种做法,但这就是我目前的做法。它适用于我,因为它允许自下而上的测试,可靠的代码覆盖以及我的类设计中的分离问题。

答案 1 :(得分:1)

在RSpec样式语法中,当我说“BDD”测试时,我想到的是,“给定”将是“描述”或“上下文”块的一部分,该块负责将测试的组件放入正确的状态,不负责在该状态下进行任何断言或验证组件的行为。

“给定”将执行设置组件的工作,然后使用“it”或“should”来验证某些行为。

describe ClassUnderTest do
  describe "some feature" do
    context "given that I am logged in" do
      before do
        sign_in Factory :user
      end
      it "has some behavior" do
        #make some assertion about the ClassUnderTest's behavior
      end
    end
  end
end

我认为如果Ruby和RSpec对您有用,Jared描述了构建BDD测试的良好结构:http://blog.carbonfive.com/2010/10/21/rspec-best-practices/