0

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

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

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

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 单元测试框架),它将断言尚未激活其帐户的用户将无法登录。当检测到此类尝试时,我会抛出一个名为UserNotActivatedException的特殊异常。问题是——因为我在控制器中捕获了所有异常,所以我的测试本身永远不会真正看到这个异常——因此测试总是会失败。我设法通过为我的模型创建特殊状态枚举来绕过这个问题,如下所示:

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

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

我的问题:

  1. 有没有更好的选择?
  2. 我也在考虑在其他控制器中使用这种设计,这里有什么缺点吗?
  3. 使用大量自定义异常来为用户显示消息是好的设计,还是使用更多的迷你 if(someCondition){return false;} 测试会更好?
4

3 回答 3

1

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

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

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

多少个例外就足够了,这取决于您。良好的异常记录可能比多样性更重要。在大多数情况下,您无论如何都不应该向用户显示来自异常的信息,而应该像“灾难性错误。如果需要帮助,错误以 ID AB455 记录”。所有“预期异常”情况都应作为正常流程处理并呈现给用户。

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

于 2013-10-28T07:44:12.607 回答
1

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

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

亲吻:保持简单(或保持简单和愚蠢):)

于 2013-10-28T08:06:36.213 回答
0

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

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

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

并像这样使用类:

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

}
// Catchblock here

这将允许您测试逻辑。

于 2013-10-28T07:45:07.047 回答