我正在编写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 等等)
我的问题:
答案 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
这将允许您测试逻辑。