我如何获得SpecFlow预期异常?
我使用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
这calculator.Add()
那将抛出一个异常,那么我该如何处理这个的方法标记[Then]
?
伟大的问题。我既不是bdd或specflow专家,但是,我的第一点建议是退后一步,评估你的情况。
你真的想在这个规范中使用术语“throw”和“exception”吗?请记住bdd的想法是在业务中使用无处不在的语言。理想情况下,他们应该能够阅读这些场景并解释它们。
考虑更改“然后”短语包括这样的事情:
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
异常仍甩在了背景,但最终的结果是一个简单的错误消息。
斯科特Bellware触及这个概念在这羊群代码播客:http://herdingcode.com/?p=176
我想补充一点,像specflow这样的BDD工具意味着与TDD结合使用。所以,你这样写你的规格,然后你会写一个单元测试,期望有一个异常。 – vintem 2011-06-14 18:52:22
很好的回答!我也不是专家,但似乎是合适的回应。 – 2015-09-03 11:04:43
作为一个新手到SpecFlow我不会告诉你,这是的方式做到这一点,但有办法做到这将是使用ScenarioContext
来存储在中抛出的异常当;
try
{
calculator.Add(1,1);
}
catch (Exception e)
{
ScenarioContext.Current.Add("Exception_CalculatorAdd", e);
}
在你然后你可以检查抛出的异常,并做断言就可以了;
var exception = ScenarioContext.Current["Exception_CalculatorAdd"];
Assert.That(exception, Is.Not.Null);
这样说;我同意scoarescoare当他说你应该制定一个更“商业友好”的措辞的情况。但是,使用SpecFlow来推动域模型的实现,捕获异常并对它们进行断言可以派上用场。
BTW:退房罗布科纳的截屏在在TekPub有关使用SpecFlow一些很好的建议:http://tekpub.com/view/concepts/5
在specflow 1.7.1.0中,您可以引用ScenarioContext.Current.TestError来查找场景期间捕获的异常。 – 2011-09-29 16:38:35
在我的上下文中,我有两种类型的'When's:抛出异常的正常情况如'当我按下Add'和一个可以处理异常的异常:'当我尝试按Add时调用相同的'WhenIPressAdd()'方法,但用'try' /'catch'块包围并像你所建议的那样处理。现在系统可以向我发出错误信息,我可以根据需要捕捉和处理它们。 – AutomatedChaos 2013-07-25 11:04:51
我在业务规则级别进行单元测试,该异常将被抛出并且从未被捕获。 - 它会被抓到上面的一个级别。所以你的解决方案对我来说非常有用。谢谢! – user3381672 2014-08-13 20:31:57
BDD可以在功能层面的行为来实践和/或在单位层面的行为。
SpecFlow是一个专注于功能级别行为的BDD工具。 例外情况不是您应该在功能级别行为中指定/观察的内容。 应该在单元级别的行为中指定/遵守例外情况。
将SpecFlow场景想象成非技术性利益相关者的实时规范。您也不会在规范中写入抛出异常,但是在这种情况下系统的行为如何。
如果您没有任何非技术利益相关者,那么SpecFlow是您的错误工具!如果没有人有兴趣阅读它们,不要浪费能量来创建商业可读规范!
有BDD工具专注于单元级别的行为。在.NET中最流行的是MSpec(http://github.com/machine/machine.specifications)。单元级的BDD也可以很容易地通过标准的单元测试框架来实践。
也就是说,你could still check for an exception in SpecFlow。
这里是BDD对单位层级与基于特征级BDD一些更多的讨论: SpecFlow/BDD vs Unit Testing BDD for Acceptance Tests vs. BDD for Unit Tests (or: ATDD vs. TDD)
也有看这个博客帖子: Classifying BDD Tools (Unit-Test-Driven vs. Acceptance Test Driven) and a bit of BDD history
所有好东西,谢谢。 – 2010-05-31 12:54:17
我了解ATDD和TDD之间的区别,如所述的博客文章中所述,但这引起我一个问题。如上所述,是不是使用BDD工具(如MSpec)就是另一个单元测试框架?在我看来,这是。此外,如果我可以对ATDD和TDD使用相同的工具,为什么不呢?这里似乎还有一些模糊的线条。 – 2010-07-27 15:07:33
嗨布莱恩 - 大多数BDD工具旨在帮助与非技术利益相关者达成共识。单元级/技术利益相关者没有太多的BDD工具,只是因为技术人员通常可以在TDD框架下进行单元级BDD。目前我使用的是NUnit,下方是英文风格的DSL。你可以做到这一点,如果它适合你。我做的唯一不同之处就是让场景步骤保持高水平,以便我可以重用它们 - 重用远远大于单元级别的重用。 – Lunivore 2010-10-16 21:59:06
改变的情况不有一个例外是让这个场景更加面向用户的好方法。然而,如果你仍然需要有它的工作,请考虑以下因素:
-
捕捉异常(我真的建议捕捉特定异常,除非你真的需要捕捉全部)在调用的操作和通步骤它到场景的上下文中。
[When("I press add")] public void WhenIPressAdd() { try { _calc.Add(); } catch (Exception err) { ScenarioContext.Current[("Error")] = err; } }
-
验证例外存储在场景方面
[Then(@"it should throw an exception")] public void ThenItShouldThrowAnException() { Assert.IsTrue(ScenarioContext.Current.ContainsKey("Error")); }
附:这与现有答案之一非常接近。但是,如果你试图从ScenarioContext使用的语法像下面获得价值:
var err = ScenarioContext.Current["Error"]
它会抛出另一个异常的情况下,如果“错误”项不存在(这将失败与正确的参数进行计算,所有场景)。所以ScenarioContext.Current.ContainsKey
可能会更恰当
如果您正在测试用户交互,我只会建议已经说过关于用户体验的话:“然后用户会看到一条错误消息”。但是,如果您正在测试UI以下的级别,我想分享我的经验:
我正在使用SpecFlow开发业务层。就我而言,我并不关心UI的交互,但我仍然发现BDD方法和SpecFlow非常有用。
在业务层,我不希望规范说“然后用户出现错误消息”,但实际上验证服务正确地响应错误的输入。我已经完成了一段时间,在“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
这让我专门编写的行动,并在一个步骤中的异常检测。我可以重复使用它来测试尽可能多的错误情况,并且它不会让我将无关的代码添加到非失败的“何时”步骤。
我是BDD的新手,但我真的不喜欢“在某事中做某事并将其放在上下文中,然后在那里读出来”的模式。我认为随着规格数量的增长,这将变得越来越难以维持,并且更多的数据被重复使用。我已经开始做你上面描述的,到目前为止我喜欢它。 – 2012-01-24 13:10:42
我的解决方案涉及到几个项目来实现的,但在最后它看起来更优雅:
@CatchException
Scenario: Faulty operation throws exception
Given Some Context
When Some faulty operation invoked
Then Exception thrown with type 'ValidationException' and message 'Validation failed'
为了使这项工作,遵循这些3个步骤:
第1步
标记您预计某些标记出现异常的场景,例如@CatchException
:
@CatchException
Scenario: ...
步骤2
定义一个AfterStep
处理程序改变ScenarioContext.TestStatus
是OK
。您可能只想忽略错误当步骤,所以你仍然可以通过测试失败,然后验证异常。已通过反射来做到这一点作为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);
}
}
步骤3
验证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);
}
嘿,你有没有发现这些答案有用? – 2011-09-28 00:22:43
@scoarescoare:是的。问题是,包含所有必需信息的正确答案是您和Kjetil的组合。你的回答说我的语言是错误的,而Kjetil实际上是说如何从'When'到Then的异常(或其他输出)。 – 2011-09-28 08:05:17
感谢您提出这个问题。我发现自己想知道同样的事情! – 2015-09-03 11:03:00