我在ASP.NET WebForms应用程序中使用Model-View-Presenter的样板实现。我的视图有两个结果事件,一个表示用户在域模型上填充了足够的字段以启动重复检查,另一个是常规的保存事件。我的伪代码如下所示:TDD可以强制创建“假”依赖关系
public class ItemNewPresenter : PresenterBase<IItemNewView>
{
public IItemService Service { get; private set; }
public IItemNewView View { get; private set; }
public ItemNewPresenter(IItemService service, IItemNewView view)
{
Service = service;
View = view;
View.OnSave += DoItemSave;
View.OnItemIsDuplicateCheck+= DoItemIsDuplicateCheck;
}
private void DoItemIsDuplicateCheck(object sender, CheckItemDuplicateEventArgs e)
{
CheckForItemDuplication(e.Item);
}
private void CheckForItemDuplication(Item item){
if (Service.IsDuplicateItem(item))
{
View.RedirectWithNotification(BuildItemUrl(item), "This item already exists");
}
}
private void DoItemSave(object sender, SaveItemEventArgs e)
{
DoItemIsDuplicateCheck(this, e.ToItemDuplicateEventArgs());
Service.Save(e.Item);
}
}
这是我确保当OnItemIsDuplicateCheck从观点提出了我演示正确的行为测试:
[Test]
public void presenter_checking_for_existing_item_should_call_redirect_if_found()
{
var service = new Mock<IItemService>();
var view = new Mock<IItemNewView>();
var presenter = new ItemNewPresenter (service.Object, view.Object);
var onCheckExistingHandler = view.CreateEventHandler <CheckItemDuplicateEventArgs>();
view.Object.OnExistingDenominatorCheck += onCheckExistingHandler;
var eventArgs = new CheckItemDuplicateEventArgs();
service.Setup(s => s.IsDuplicate(It.Is<CheckItemDuplicateEventArgs>(c => c.Equals(eventArgs)))).Returns(true);
onCheckExistingHandler.Raise(eventArgs);
view.Verify(v => v.RedirectWithNotification(It.IsAny<String>(), It.IsAny<string>()), Times.Once());
service.Verify();
}
为了保持一致性,我想有同样的重复当View引发OnSave
事件时检查被触发。我的问题是,当我要验证的方法之一(CheckForItemDuplication
)在被测试的类上声明时,我应该如何写我的测试。验证SUT方法调用(坏)的替代方法是用重复代码编写我的保存测试批次(我的所有嘲笑的设置和断言都将从上述测试中复制),并且还会使单元测试不那么专注。
[Test]
public void presenter_saving_item_should_check_for_dupe_and_save_if_not_one() {
//duplicate mocks/setups/asserts from duplicate check fixture
//additional mocks/setups/asserts to test save logic
}
我认为 TDD建议拉这个私有方法到一个单独的类,它与我的演讲合作,并会通过DI注入。但是,向我的Presenter添加另一个依赖关系,看起来不值得作为独立抽象的功能*和*表示我的Presenter的内部实现细节,看起来......好吧......太疯狂了。我在这里基地?必须有一些设计模式或重构我可以应用,这将避免需要将私有方法变成依赖项。
有趣的方法,但我觉得必须有更好的方法。正如你所指出的,我试图测试的代码是SUT的私有实现细节,并且永远不需要通过消费类注入。引入一个新的类来封装内部行为只是为了实现免费的测试代码似乎彻头彻尾的疯狂:( – 2011-02-26 17:22:55