如何让SpecFlow期待异常?

Rog*_*mbe 40 expected-exception specflow

我正在使用SpecFlow,我想写一个如下的场景:

Scenario: Pressing add with an empty stack throws an exception
    Given I have entered nothing into the calculator
    When I press add
    Then it should throw an exception
Run Code Online (Sandbox Code Playgroud)

calculator.Add()将是一个例外,所以如何在标记的方法中处理这个[Then]

Sco*_*tes 40

好问题.我不是bdd或specflow专家,但是,我的第一点建议是退后一步并评估你的情景.

你真的想在这个规范中使用术语"抛出"和"异常"吗?请记住,bdd的想法是使用无处不在的语言与业务.理想情况下,他们应该能够阅读这些场景并解释它们.

考虑更改"then"短语以包含以下内容:

Scenario: Pressing add with an empty stack displays an error
    Given I have entered nothing into the calculator
    When I press add
    Then the user is presented with an error message
Run Code Online (Sandbox Code Playgroud)

异常仍然在后台抛出,但最终结果是一个简单的错误消息.

Scott Bellware在这个Herding Code播客中触及了这个概念:http://herdingcode.com/?p = 176

  • 我想补充一点,像specflow这样的BDD工具意味着要与TDD一起使用.所以你像这样编写你的规范然后你会编写一个期望异常的单元测试. (14认同)

Kje*_*sen 37

作为SpecFlow的新手,我不会告诉你这是这样做方法,但一种方法是使用ScenarioContextfor存储在When中抛出的异常;

try
{
    calculator.Add(1,1);
}
catch (Exception e)
{
    ScenarioContext.Current.Add("Exception_CalculatorAdd", e);
}
Run Code Online (Sandbox Code Playgroud)

在你的那个你可以检查抛出的异常并对其进行断言;

var exception = ScenarioContext.Current["Exception_CalculatorAdd"];
Assert.That(exception, Is.Not.Null);
Run Code Online (Sandbox Code Playgroud)

照这样说; 我同意scoarescoare,他说你应该用更加"商业友好"的措辞来制定场景.但是,使用SpecFlow来驱动域模型的实现,捕获异常并对它们执行断言可以派上用场.

顺便说一下:查看Rob Conery在TekPub上的截屏视频,了解使用SpecFlow的一些非常好的提示:http://tekpub.com/view/concepts/5

  • 在specflow 1.7.1.0中,您可以为场景中捕获的异常引用ScenarioContext.Current.TestError. (2认同)

jba*_*ndi 14

可以在特征级别行为或/和单元级别行为上实践BDD.

SpecFlow是一个专注于功能级行为的BDD工具.例外不是您应该在功能级别行为上指定/观察的内容.应在单元级行为上指定/观察异常.

将SpecFlow场景视为非技术利益相关方的实时规范.您也不会在规范中写入抛出异常,而是在这种情况下系统的行为方式.

如果您没有任何非技术利益相关者,那么SpecFlow对您来说是错误的工具!如果没有人有兴趣阅读它们,请不要浪费精力创建商业可读规范!

BDD工具专注于单元级行为.在.NET中,最受欢迎的是MSpec(http://github.com/machine/machine.specifications).单元级别的BDD也可以通过标准单元测试框架轻松实现.

也就是说,您仍然可以在SpecFlow中检查异常.

以下是关于功能级单元级与bdd的bdd的更多讨论: SpecFlow/BDD与单元测试 BDD接受测试与BDD单元测试(或:ATDD与TDD)

另请看这篇博客文章: 分类BDD工具(单元测试驱动与验收测试驱动)和一些BDD历史

  • 即使每个看测试的人都是纯技术人员,也可以使用Specflow.我的团队成员和我自己发现使用Specflow编写的测试非常直观,易于编写和重复使用.它们不是浪费能源. (3认同)

小智 7

更改场景不会出现异常可能是让场景更加面向用户的好方法.但是,如果您仍需要使用它,请考虑以下事项:

  1. 在调用操作并将其传递给场景上下文的步骤中捕获异常(我真的建议捕获特定异常,除非您确实需要捕获所有异常).

    [When("I press add")]
    public void WhenIPressAdd()
    {
       try
       {
         _calc.Add();
       }
       catch (Exception err)
       {
          ScenarioContext.Current[("Error")] = err;
       }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 验证该异常存储在方案上下文中

    [Then(@"it should throw an exception")]
    public void ThenItShouldThrowAnException()
    {
          Assert.IsTrue(ScenarioContext.Current.ContainsKey("Error"));
    }
    
    Run Code Online (Sandbox Code Playgroud)

PS它非常接近现有的答案之一.但是,如果您尝试使用如下语法从ScenarioContext获取值:

var err = ScenarioContext.Current["Error"]
Run Code Online (Sandbox Code Playgroud)

如果"Error"键不存在,它将抛出另一个异常(并且将使用正确参数执行计算的所有场景失败).所以ScenarioContext.Current.ContainsKey可能更恰当


Fra*_*lls 5

如果您正在测试用户交互,我只会建议关于用户体验的内容:"然后会向用户显示错误消息".但是,如果您正在测试UI以下的级别,我想分享我的经验:

我正在使用SpecFlow开发业务层.在我的情况下,我不关心UI交互,但我仍然发现BDD方法和SpecFlow非常有用.

在业务层中,我不希望规范说"然后向用户显示错误消息",但实际上验证服务是否正确响应了错误的输入.我已经做了一段时间已经说过在"When"中捕获异常并在"Then"验证它,但我发现这个选项不是最佳的,因为如果你重用"When"步骤你可以吞下你没想到的例外.

目前,我使用明确的"Then"子句,有时没有"When",这样:

Scenario: Adding with an empty stack causes an error
     Given I have entered nothing into the calculator
     Then adding causes an error X
Run Code Online (Sandbox Code Playgroud)

这允许我在一个步骤中专门编写动作和异常检测.我可以重复使用它来测试尽可能多的错误情况,并且它不会让我在非失败的"When"步骤中添加不相关的代码.

  • 我是BDD的新手,但我真的不喜欢"在When中做某事并在上下文中抛出然后在Then中读出"的模式.我认为随着规格数量的增加,这将越来越难以维持,并且随着更多的规格被重复使用.我已经开始做你上面描述的内容,到目前为止我很喜欢它. (2认同)

Vit*_*kov 5

我的解决方案涉及几个要实现的项目,但最终看起来会更加优雅:

@CatchException
Scenario: Faulty operation throws exception
    Given Some Context
    When Some faulty operation invoked
    Then Exception thrown with type 'ValidationException' and message 'Validation failed'
Run Code Online (Sandbox Code Playgroud)

为此,请按照以下3个步骤操作:

步骤1

用一些标记标记您希望出现异常的方案,例如@CatchException

@CatchException
Scenario: ...
Run Code Online (Sandbox Code Playgroud)

第2步

定义AfterStep要更改ScenarioContext.TestStatus为的处理程序OK。您可能只想忽略for when步骤中的错误,因此您仍然可以在The then verifying an exception中使测试失败。必须通过反射来完成此操作,因为TestStatus属性是内部的:

[AfterStep("CatchException")]
public void CatchException()
{
    if (ScenarioContext.Current.StepContext.StepInfo.StepDefinitionType == StepDefinitionType.When)
    {
        PropertyInfo testStatusProperty = typeof(ScenarioContext).GetProperty("TestStatus", BindingFlags.NonPublic | BindingFlags.Instance);
        testStatusProperty.SetValue(ScenarioContext.Current, TestStatus.OK);
    }
}
Run Code Online (Sandbox Code Playgroud)

第三步

验证TestError方式与验证内的任何方式相同ScenarioContext

[Then(@"Exception thrown with type '(.*)' and message '(.*)'")]
public void ThenExceptionThrown(string type, string message)
{
    Assert.AreEqual(type, ScenarioContext.Current.TestError.GetType().Name);
    Assert.AreEqual(message, ScenarioContext.Current.TestError.Message);
}
Run Code Online (Sandbox Code Playgroud)