我不认为这是目前可行的,或者它是否是一个好主意,但这是我刚才想到的。我使用MSTest对我的C#项目进行单元测试。在我的一个测试中,我执行以下操作:
MyClass instance;
try
{
instance = getValue();
}
catch (MyException ex)
{
Assert.Fail("Caught MyException");
}
instance.doStuff(); // Use of unassigned local variable 'instance'
要编译此代码,我必须在其声明或instance
块中为catch
分配值。我可以在return
之后Assert.Fail
,但这仍然是一种解决方法而不是编译器只是知道在此之后执行无法继续。据我所知,Assert.Fail
永远不会允许执行过去,因此instance
永远不会没有价值。那为什么我必须为它分配一个值?如果我将Assert.Fail
更改为类似throw ex
的内容,代码编译得很好,我假设因为它知道异常将禁止执行进入instance
未初始化的点。< / p>
相反,如果我不希望测试失败,而是标记为不确定,该怎么办?我可以做一个Assert.Inconclusive
而不是Fail
,如果编译器知道在此之后执行不会继续,那就太好了。
这是运行时与编译时知识有关允许执行的位置的情况吗? C#是否有理由说某个成员(在这种情况下为Assert.Fail
)在返回后永远不允许执行?也许这可能是方法属性的形式。这会对编译器有用还是不必要的复杂性?
外部单元测试
由于人们[有效]指出这是编写单元测试的愚蠢方式,因此请考虑我在单元测试领域之外的问题:
MyClass instance;
if (badThings)
{
someMethodThatWillNeverReturn();
}
else
{
instance = new MyClass();
}
instance.doStuff();
这里可能我可以用抛出异常替换对someMethodThatWillNeverReturn
的调用,也许如果我有事可做,我可以在构造函数中为异常做。
Resharper知道
如果我在return
或Assert.Fail
之后添加Assert.Inconclusive
,则Resharper会将return
颜色设置为灰色,并提供一个工具提示“代码是启发式无法访问的。”
答案 0 :(得分:3)
是的,有一些东西表明一个成员永远不会正常完成是合理的 - 即断言成员之后的点是无法到达的。 (这可能是由于异常或由于永远循环造成的。)
你希望有某些东西(无论是在CLR还是编译器中)为你提出一个备份计划,如果你错了:如果有人改变了会发生什么{{1正常返回?您可能希望部分代码验证成为检查它永远无法正常返回的内容。
我相信有一篇关于这个想法的博客文章来自微软的某个人...我会看看我是否能找到它。
就表示它的语法而言,虽然属性是一个明显的想法,但我非常喜欢返回类型为“never”的想法。显然,这可能会与现有的“从不”类型发生冲突,但是嘿......
就其实用性而言:显而易见的解决方法是在语句之后立即抛出异常,但是必须这样做肯定很烦人。 (它通常比返回更好,因为这意味着如果您正在编写此方法的方法具有返回类型,则不必指定无意义的返回值 - 并且您也不需要确保所有{{ 1}}参数被赋值。)所以它不是必需的 - 但我认为它会很好。 C#团队在预算有限的情况下能做的最重要的事情是另一回事 - 只是为了抢占Eric;)
答案 1 :(得分:1)
throw ex
之后 Assert.Fail
?或者,更好的是,在这种情况下完全删除try/catch
和Assert.Fail
,并让未捕获的异常为您进行单元测试失败。
答案 2 :(得分:1)
我想知道的一件事:如果您手动编译并执行它,运行时如何处理此代码?我相信它会检查IL代码中的错误,也许它会抓住这个“错误”......也许不会:)
答案 3 :(得分:0)
return
之后 Assert.Fail()
答案 4 :(得分:0)
看到你在抛出异常时断言失败,为什么不将整个测试移动到try中,或者除掉try并让抛出的异常自动断言失败。
try
{
MyClass instance;
instance = MyClass.getValue();
instance.doStuff();
}
catch (Exception ex)
{
Assert.Fail("Caught MyException");
}
......或......
MyClass instance;
instance = MyClass.getValue();
instance.doStuff();