我正在使用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]
的方法中处理此问题?
答案 0 :(得分:39)
好问题。我不是bdd或specflow专家,但是,我的第一点建议是退后一步并评估你的情景。
你真的想在本规范中使用“throw”和“exception”这两个术语吗?请记住,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
异常仍在后台抛出,但最终结果是一条简单的错误消息。
Scott Bellware在这个Herding Code播客中触及了这个概念:http://herdingcode.com/?p=176
答案 1 :(得分:37)
作为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来驱动域模型的实现,捕获异常并对它们执行断言可以派上用场。
顺便说一句:在TekPub上查看Rob Conery的截屏视频,了解使用SpecFlow的一些非常好的提示:http://tekpub.com/view/concepts/5
答案 2 :(得分:14)
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
答案 3 :(得分:7)
更改场景不会出现异常可能是让场景更加面向用户的好方法。但是,如果您仍需要使用它,请考虑以下事项:
在调用操作并将其传递给场景上下文的步骤中捕获异常(我真的建议捕获特定的异常,除非你真的需要捕获所有异常)。
[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"));
}
P.S。它非常接近现有的答案之一。但是,如果您尝试使用如下语法从ScenarioContext获取值:
var err = ScenarioContext.Current["Error"]
如果“Error”键不存在,它将抛出另一个异常(并且将使用正确参数执行计算的所有场景失败)。所以ScenarioContext.Current.ContainsKey
可能更合适
答案 4 :(得分: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
这允许我在一个步骤中专门编写动作和异常检测。我可以重复使用它来测试尽可能多的错误情况,它不会让我在非失败的“When”步骤中添加不相关的代码。
答案 5 :(得分:4)
我的解决方案涉及几个要实现的项目,但最后它看起来会更优雅:
@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
。您可能只希望忽略 When 步骤中的错误,因此您仍然可以在然后验证异常时使测试失败。不得不通过反射来做到这一点,因为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);
}
答案 6 :(得分:0)
ScenarioContext.Current在SpecFlow的最新版本中已被弃用,现在建议向构造函数中的步骤测试类添加POCO,以存储/检索步骤之间的上下文,即
public class ExceptionContext
{
public Exception Exception { get; set; }
}
private ExceptionContext _context;
public TestSteps(ExceptionContext context)
{
_context = context;
}
然后在[When]绑定中......
try
{
// do something
}
catch (MyException ex)
{
_context.Exception = ex;
}
在[Then]绑定中,断言_context.Exception已设置且具有您期望的异常类型。