【问题标题】:ASP.NET MVC - Unit testing overkill? (TDD)ASP.NET MVC - 单元测试矫枉过正? (TDD)
【发布时间】:2010-11-25 04:45:58
【问题描述】:

所以我开始发现 TDD 错误,但我想知道我是否真的做对了......我似乎正在编写 A LOT 的测试。

当然,测试越多越好,但我有一种感觉,我已经做完了。老实说,我不知道我还能坚持写这些简单的重复测试多久。

例如,这些是来自我的 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,
查尔斯

【问题讨论】:

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


    【解决方案1】:

    这完全取决于您需要/想要多少覆盖范围以及有多少可靠性是一个问题。

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

    • 此单元测试是否有助于实现我还没有的功能/代码更改?
    • 如果我稍后进行更改,此单元测试是否有助于回归测试/调试此单元?
    • 满足此单元测试的代码是重要的还是值得进行单元测试?

    关于第 3 个,我记得当我开始编写单元测试时(我知道,这与 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/

    【讨论】:

    • 是的,听起来这些问题的答案应该随着经验变得更容易。有趣的是你应该提到史蒂夫,因为他的 MVC 书目前在我的床头柜上……昨晚完成了第 1 章! ;-) 听起来我应该坚持阅读它。
    • @Charlino,我刚下单了!在阅读了评论和他的一些帖子之后,我很兴奋!希望这会有所帮助。
    【解决方案2】:

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

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

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

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

    您可以通过一个不提供参数的测试来实现对该方法行为的测试。这将影响例程的“设置默认值”部分,同时仍会测试其余代码是否也表现良好。

    【讨论】:

    • 听起来是个很好的建议——我会把它带回家仔细研究。回复:硬编码“/”测试 - 是的,就像一个流鼻涕的鼻子一样挑选它! ;-)
    • 请记住,我并不是在提倡您不要在测试中对某些值进行硬编码,尽管一些纯粹主义者不同意。在那种情况下,我可能也会对“/”进行硬编码测试......我只是主张你的测试不应该只测试值。
    【解决方案3】:

    这对我来说似乎是正确的。是的,你会写很多单元测试,最初,这看起来有点矫枉过正,TBH 是在浪费时间;但坚持下去,这将是值得的。您的目标应该是 100% 的功能覆盖率(而不仅仅是 100% 的代码覆盖率)。但是...如果您发现您正在为同一方法编写大量 UT,则该方法可能做得太多。尝试更多地分离您的关注点。以我的经验,Action 的主体应该只是更新一个类来完成真正的工作。你真正应该用 UT 瞄准的就是这个类。

    克里斯

    【讨论】:

    • 好的,所以你是说所有这些测试都有一席之地?关于编写太多测试并分离您的关注点的问题......我认为我对我的 post 方法进行了太多测试,并且(如果我自己这么说的话)关注点分离是正确的。我想这确实表明我正在编写太多测试......?我一定会坚持下去,而且我认为通过更多的经验,我将能够更容易地辨别什么和不测试(正如你和许多其他人所暗示的那样)。
    【解决方案4】:

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

    我个人不是 100% TDD(有时也太懒了),但如果你打算做到 100%,也许你应该写一些测试助手来减轻这些重复测试的负担。例如,编写一个帮助程序来测试标准 post 结构中的所有 CRUD,并带有回调以允许您通过一些评估可能会节省大量时间。

    【讨论】:

      【解决方案5】:

      我只对我不确定的部分进行单元测试。当然 - 你永远不知道什么会刺伤你,但为琐碎的事情编写测试对我来说似乎有点过头了。

      我不是单元测试/tdd 专家——但我认为,如果你不编写测试只是为了拥有它们,那很好。它们一定是有用的。如果您对单元测试有足够的经验 - 您会开始感觉它们什么时候有价值,什么时候不有价值。

      你可能会喜欢this book

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

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

      【讨论】:

      • 我相信我会掌握它的窍门,并且在编写了相当多的测试之后,我将能够决定要测试什么,不测试什么。哦,是的,我有那本书,刚读完……读得很好,会推荐给其他人。但我真的不记得关于测试的确切内容和测试量的任何建议。
      • 刚刚又查了一遍(书)——看来我的记忆有点乱了。在别处见过这个。对不起。 :)
      猜你喜欢
      • 1970-01-01
      • 2014-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-17
      • 2013-01-10
      相关资源
      最近更新 更多