这是一个糟糕的设计吗?

Ken*_*ran 5 c# bdd unit-testing

我正在尝试行为驱动的开发,我发现自己在我写作时第二次猜测我的设计.这是我的第一个绿地项目,可能只是我缺乏经验.无论如何,这是我正在编写的类的简单规范.它是用BDD风格在NUnit中编写的,而不是使用专用的行为驱动框架.这是因为该项目的目标是.NET 2.0,所有的BDD框架似乎都采用了.NET 3.5.

[TestFixture]
public class WhenUserAddsAccount
{
    private DynamicMock _mockMainView;
    private IMainView _mainView;

    private DynamicMock _mockAccountService;
    private IAccountService _accountService;

    private DynamicMock _mockAccount;
    private IAccount _account;

    [SetUp]
    public void Setup()
    {
        _mockMainView = new DynamicMock(typeof(IMainView));
        _mainView = (IMainView) _mockMainView.MockInstance;

        _mockAccountService = new DynamicMock(typeof(IAccountService));
        _accountService = (IAccountService) _mockAccountService.MockInstance;

        _mockAccount = new DynamicMock(typeof(IAccount));
        _account = (IAccount)_mockAccount.MockInstance;
    }

    [Test]
    public void ShouldCreateNewAccount()
    {
        _mockAccountService.ExpectAndReturn("Create", _account);
        MainPresenter mainPresenter = new MainPresenter(_mainView, _accountService);
        mainPresenter.AddAccount();
        _mockAccountService.Verify();
    }
}
Run Code Online (Sandbox Code Playgroud)

MainPresenter使用的接口都没有任何实际的实现.AccountService将负责创建新帐户.可以将IAccount的多个实现定义为单独的插件.在运行时,如果有多个,则会提示用户选择要创建的帐户类型.否则AccountService将只创建一个帐户.

让我感到不安的一件事是,只需编写一个规范/测试就需要多少次模拟.这只是使用BDD的副作用还是我错误的方式?

[更新]

这是MainPresenter.AddAccount的当前实现

    public void AddAccount()
    {
        IAccount account;
        if (AccountService.AccountTypes.Count == 1)
        {
            account = AccountService.Create();
        }
        _view.Accounts.Add(account);
    }
Run Code Online (Sandbox Code Playgroud)

任何提示,建议或替代方案欢迎.

Sha*_*lle 3

当进行自上而下的开发时,发现自己使用大量模拟是很常见的。你需要的部分不存在,所以你自然需要嘲笑它们。话虽如此,这确实感觉像是一个验收水平测试。根据我的经验,BDD 或上下文/规范在单元测试级别开始变得有点奇怪。在单元测试级别,我可能会做更多类似的事情......

添加帐户时
   应该使用帐户服务来创建新帐户
   should_update_screen_with_new_account_details

您可能需要重新考虑 IAccount 接口的使用。我个人坚持在域实体上保留服务接口。但这更多的是个人喜好。

其他一些小建议...

  • 您可能需要考虑使用模拟框架,例如 Rhino Mocks(或 Moq),它允许您避免在断言中使用字符串。
  _mockAccountService.Expect(模拟=>模拟.Create())
     .Return(_account);

  • 如果您正在使用 BDD 风格,我见过的一种常见模式是使用链式类进行测试设置。在你的例子中...
公共类 MainPresenterSpec
{
    // Mock 的受保护变量

    [设置]
    公共无效设置()
    {
       // 设置模拟
    }

}

[测试治具]
公共类 WhenUserAddsAccount :MainPresenterSpec
{
    [测试]
    公共无效应该创建新帐户()
    {
    }
}
  • 另外我建议更改您的代码以使用保护子句。
     公共无效添加帐户()
     {
        if (AccountService.AccountTypes.Count != 1)
        {
            // 在这里做你想做的事。留言?
        返回;
        }

    IAccount 帐户 = AccountService.Create();

        _view.Accounts.Add(帐户);
     }