【问题标题】:Given-When-Then when dealing with User InputGiven-When-Then 处理用户输入时
【发布时间】:2013-10-05 05:53:30
【问题描述】:

我正在寻找有关 Arrange/Act/Assert 的一些说明,我在开发过程中将其作为 Given-When-Then 实施。我试图坚持这个概念,但我发现在某些事件(特别是用户输入)期间,我必须重新考虑将“Act”动作作为“Arrange”动作,以便在单元测试中正确捕获它.我在这里使用 Moq 作为我的模拟框架。

例如:在我的项目中,代码的焦点是用户提供的图像。存在用户可以选择图像的功能,但如果图像已经存在,它将询问用户是否要替换活动图像,或者取消操作并保留活动图像。我觉得编写这个特定场景的正确方式是:

给定一个已经存在图像的工作区
用户请求新图像
并且用户选择替换活动图像
那么程序应该替换图像

Testwise,它看起来像这样:

mockModel.SetupProperty(m => m.Image, new Bitmap(100, 100));  // Given
mockView.Raise(v => v.UserRequestsNewImage += null);          // When
mockMBox.Setup(mb => mb.ViewResult).Returns(ViewResult.OK);   // And
mockView.Verify(v => v.OpenAddImageFileDialog(), Times.Once); // Then

代码方面,在我的演示者中,它看起来像这样:

private void view_UserRequestsNewImage()
{
  if (model.Image != null)
  {
    mbox.ShowDialog();

    if (mbox.ViewResult == ViewResult.Cancel)
      return;
  }

  view.OpenAddImageFileDialog();
}

但这失败了,因为消息框的Setup 发生在视图的Raise 被调用之后。因此,我需要将Setup 移到它之前(并且使用Setup 让它感觉像是一个“排列”设置):

给定一个已经存在图像的工作区
并且用户选择替换活动图像
用户请求新图像
那么程序应该替换图像

但是现在,我的场景感觉不正常,而且流程不正确。我觉得用户选择替换图像(Setup),因为它发生在用户选择添加新图像(Raise)之后,应该是Act步骤的一部分,但为了让它正确模拟,我需要把它放在排列步骤中。

我在这里使用了错误的模拟框架吗?有一个更好的方法吗?还是我不切实际地担心用户输入步骤在 Given-When-Then 设置中的位置?

提前致谢。

【问题讨论】:

  • 使用一些自定义扩展方法,您可以将您的 When-And's 链接在一起,这样您就可以按照逻辑顺序编写它们,而不是直接调用 Moq,而是将 lambda's 放入堆栈并在之后以相反的顺序执行它们终止表达式。

标签: c# user-input bdd mvp arrange-act-assert


【解决方案1】:

“当用户请求新图像时”用户可以选择替换当前图像。

所以你可以重写“Given, When, Then”来阅读:

  • 给定一个已经存在图像的工作区
  • 用户选择替换活动图像时
  • 那么程序应该替换图像

即省略表示“用户请求新图像”的“时间”,因为他们在替换当前图像时无论如何都必须这样做。

我还放弃了 cmets(代码有异味,尽管它们并不多!),并将这些测试步骤放入如下的小方法中:

void GivenImageAlreadyPresent()
{
 mockModel.SetupProperty(m => m.Image, new Bitmap(100, 100));  
}

void WhenActiveImageReplaced()
{
 mockMBox.Setup(mb => mb.ViewResult).Returns(ViewResult.OK);   
 mockView.Raise(v => v.UserRequestsNewImage += null);          
}

void ThenImageShouldBeReplaced()
{
 mockView.Verify(v => v.OpenAddImageFileDialog(), Times.Once);
}

void Test()
{
 GivenImageAlreadyPresent();
 WhenActiveImageReplaced();
 ThenImageShouldBeReplaced();
}

这使得实际测试的可读性更好(即它现在可以自我记录),并且可以在需要时重复使用这些步骤。

【讨论】:

  • 嗯,这是一个相当简单、直截了当的“我为什么不这么想”的解决方案。谢谢你。单词选择使一切变得不同!并且记录在案 - 我已经完全按照您的方式做了(在方法中放置操作),我只是想让我的问题保持简单。我最初尝试将 SpecFlow 用于 BDD,但它并没有随我而行,所以我决定像这样手动处理所有方法,我发现它运行良好。再次感谢!
  • @Arclight 很高兴我能帮上忙 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多