ASP.NET MVC - 单元测试过度杀伤? (TDD)

时间:2009-09-03 06:47:40

标签: asp.net-mvc unit-testing tdd

所以我开始抓住TDD错误,但我想知道我是否真的做得对......我似乎在写很多的测试。

越多的测试越好,当然,但我有一种感觉,我已经过度了。说实话,我不知道我能用多长时间来编写这些简单的重复测试。

例如,这些是来自我的AccountController的LogOn操作:

public ActionResult LogOn(string returnUrl)
{
    if (string.IsNullOrEmpty(returnUrl))
        returnUrl = "/";

    var viewModel = new LogOnForm()
    {
        ReturnUrl = returnUrl
    };

    return View("LogOn", viewModel);
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(LogOnForm logOnForm)
{
    try
    {
        if (ModelState.IsValid)
        {
            AccountService.LogOnValidate(logOnForm);

            FormsAuth.SignIn(logOnForm.Email, logOnForm.RememberMe);

            return Redirect(logOnForm.ReturnUrl);
        }
    }
    catch (DomainServiceException ex)
    {
        ex.BindToModelState(ModelState);
    }
    catch
    {
        ModelState.AddModelError("*", "There was server error trying to log on, try again. If your problem persists, please contact us.");
    }

    return View("LogOn", logOnForm);
}

非常自我解释。

然后我有以下测试套件

public void LogOn_Default_ReturnsLogOnView()
public void LogOn_Default_SetsViewDataModel()
public void LogOn_ReturnUrlPassedIn_ViewDataReturnUrlSet()
public void LogOn_ReturnUrlNotPassedIn_ViewDataReturnUrDefaults()
public void LogOnPost_InvalidBinding_ReturnsLogOnViewWithInvalidModelState()
public void LogOnPost_InvalidBinding_DoesntCallAccountServiceLogOnValidate()
public void LogOnPost_ValidBinding_CallsAccountServiceLogOnValidate()
public void LogOnPost_ValidBindingButAccountServiceThrows_ReturnsLogOnViewWithInvalidModelState()
public void LogOnPost_ValidBindingButAccountServiceThrows_DoesntCallFormsAuthServiceSignIn()
public void LogOnPost_ValidBindingAndValidModelButFormsAuthThrows_ReturnsLogOnViewWithInvalidModelState()
public void LogOnPost_ValidBindingAndValidModel_CallsFormsAuthServiceSignIn()
public void LogOnPost_ValidBindingAndValidModel_RedirectsToReturnUrl()

那过度杀人吗?我甚至没有展示过服务测试!

我可以剔除哪些(如果有的话)?

TIA,
查尔斯

5 个答案:

答案 0 :(得分:17)

这完全取决于您需要/需要的覆盖范围以及可靠性是多少。

以下是您应该问自己的问题:

  • 此单元测试是否有助于实现我尚未拥有的功能/代码更改?
  • 如果我稍后进行更改,此单元测试是否会帮助回归测试/调试此单元?
  • 满足此单元测试的代码是否非常重要或是否值得进行单元测试?

关于第三个,我记得当我开始编写单元测试时(我知道,与TDD不同)我会进行类似的测试:

string expected, actual;
TypeUnderTest target = new TypeUnderTest();
target.PropertyToTest = expected;
actual = target.PropertyToTest;
Assert.AreEqual<string>(expected, actual);

我可以用我的时间做更高效的事情,例如为我的桌面选择更好的壁纸。

我推荐ASP.net MVC书籍作者Sanderson的这篇文章:

http://blog.codeville.net/2009/08/24/writing-great-unit-tests-best-and-worst-practises/

答案 1 :(得分:5)

我会说你做得比你可能要多一点。虽然测试代码可能采用的每个可能路径都很好,但某些路径不是很重要,或者不会导致行为的真正差异。

在您的示例中,使用LogOn(string returnUrl)

您要做的第一件事是检查returnUrl参数,如果它为null / empty,则将其重新分配给默认值。你真的需要一个完整的单元测试来确保一行代码按预期发生吗?这不是一条容易破裂的路线。

可能会破坏该行的大多数更改都会导致编译错误。可以在该行中更改所分配的默认值(可能您稍后决定“/”不是一个好的默认值...但在您的单元测试中,我打赌您硬编码以检查“/” “不是吗?因此,价值的变化将需要改变你的测试......这意味着你没有测试你的行为,而是测试你的数据。

您可以通过简单地使用一个不提供参数的测试来测试方法的行为。这将触及例程的“设置默认”部分,同时仍然测试其余代码的行为也是如此。

答案 2 :(得分:3)

看起来对我来说是正确的。是的,你会写很多单元测试,最初看起来像矫枉过正和TBH浪费时间;但坚持下去,这是值得的。你应该瞄准的是(而不仅仅是100%的代码覆盖率)是100%的功能覆盖率。但是......如果你发现你为同一个方法编写了很多UT,那么这个方法可能做得太多了。尝试更多地分离您的顾虑。根据我的经验,行动的主体应该做的不仅仅是新建一个班级来完成真正的工作。你应该真正用UT来定位这个类。

克里斯

答案 3 :(得分:2)

我只对我不确定的代码进行单元测试。当然 - 你永远不会知道什么会反击你,但为琐碎的事情编写测试对我来说似乎是一种矫枉过正。

我不是单元测试/ tdd大师 - 但我认为如果你不编写测试就可以了。它们必须有用。如果您对单元测试有足够的经验 - 那么当它们变得有价值时,您就会开始感觉

您可能会喜欢this book

编辑:
实际上 - 我刚刚在隔离框架章节下面找到了关于这个的引用。这是关于过度指定测试(一个特定测试),但我想这个想法仍然在更全球范围内:

  

过度指定测试
  如果您的测试有太多期望,您可以创建一个测试   即使是最轻微的代码更改也会崩溃   整体功能仍然有效。考虑一下这种技术方式   没有验证正确的事情。测试交互是一个双刃剑   剑:测试太多,你开始忽视大局 - 整体功能;测试它太少了,你会想念的   对象之间的重要互动。

答案 4 :(得分:2)

100%的覆盖率是非常理想的,但是如果您必须大规模重构代码,它真的很有用,因为测试将控制您的代码规范以确保它是正确的。

我个人不是100%TDD(有时也太懒)但是如果你打算做100%,也许你应该写一些测试助手来消除这些重复测试的负担。例如,编写一个帮助程序来测试标准发布结构中的所有CRUD并使用回调来允许您传递一些评估可能会为您节省大量时间。