异常和测试 - 当我抓住它们时,如何对特定异常进行单元测试?

时间:2013-10-28 07:36:14

标签: c# unit-testing asp.net-mvc-4 exception

我正在编写MVC4 Web应用程序。通常我尝试在每个将ActionResult返回给用户的控制器方法中放入“try {} catch {}”块。我这样做是为了捕获所有异常并显示相应的消息,因此用户永远不会看到如下内容:

“引用未设置为对象的实例”

我的控制器通常如下所示:

try
{

}
catch(MyFirstCustomException ex)
{
//set some message for the user and do some cleaning etc.

return ActionResult();
}
catch(MySecondCustomException ex) (and so on...)
{
//set some message for the user and do some cleaning etc.

return ActionResult();
}
catch(Exception ex)
{
//set some message for the user and do some cleaning etc.

return ActionResult();
}

但是现在我遇到了以下情况:我有 AccountController LogIn 方法,我想编写一个单元测试(使用Microsoft Unit Testing Framework),声明未激活其帐户的用户将无法登录。当检测到此类尝试时,我会抛出一个名为 UserNotActivatedException 的特殊异常。问题是 - 因为我在控制器中捕获了所有异常,我的测试本身永远不会看到这个异常 - 因此测试总是会失败。我设法通过为我的模型创建特殊的状态枚举来绕过这个问题,如下所示:

public enum LoginViewModelStatus
{
NotLoggedIn = 0,
LoginSuccessfull = 1,
LoginFailed = 2,
UserNotActivatedException = 3,
UnknownErrorException = 100
}

并通过在发生某事时将其设置为某个值(所以当我捕获我的特殊UserNotActivatedException时 - 我将 loginModelStatus设置为UserNotActivatedException 等等)

我的问题:

  1. 还有更好的选择吗?
  2. 我正在考虑在其他控制器中使用这种设计,这里有任何垮台吗?
  3. 使用大量自定义异常来为用户显示消息是不是很好的设计,或者使用更多mini如果(someCondition){return false;}测试会更好吗?

3 个答案:

答案 0 :(得分:1)

您应该测试该代码在所有情况下都返回预期结果,并且或多或少地忽略方法的工作方式。

即。在您的情况下Controller将多个异常转换为不同的视图 - 测试当您提供导致异常情况的数据时,Controller会返回您期望的视图。

如果控制器使用的较低级别的方法可能会抛出异常 - 也会测试它们,但这次是为了抛出特殊异常。

由您决定有多少例外就足够了。良好的异常记录可能比变化更重要。在大多数情况下,您不应该向用户显示异常信息,而是“灾难性错误。如果需要帮助,则会记录ID为AB455的错误”。应处理所有“预期异常”情况,并将其作为正常流程呈现给用户。

请注意,只要您拥有处理所有异常的代码,就可以从操作中抛出异常。像HandleErrorAttribute这样的操作过滤器可用于为特定操作/整个应用程序配置例外策略。

答案 1 :(得分:1)

您可以将代码包装在try部件中,以便能够对此部件进行单元测试。 在这里,单元可测试部分简单地“包裹”在MyUnitTestableMethod方法中:

try
{
    MyUnitTestableMethod();
}
catch(MyFirstCustomException ex)
{
    // ...
}
catch(MySecondCustomException ex) (and so on...)
{
    // ...
}
catch(Exception ex)
{
    // ...
}

KISS:保持简单(或保持简单和愚蠢):)

答案 2 :(得分:0)

看起来你的代码“太稳定了”。也就是说,您的逻辑永远不会产生错误。从稳定性的角度来看这是好的,但不是很可测试的。

在这种情况下,我会有一个类来处理自定义逻辑,在返回ActionResult以分离逻辑之前捕获从该类生成的所有异常。

class ActionClass
{
    public bool HandleLogin(...)
    {
        ...
    }
}

并使用这样的类:

try
{
    ActionClass action = new ActionClass();
    action.HandleLogin(...)

}
// Catchblock here

这将允许您测试逻辑。

相关问题