单元测试 - 不是可测试代码转换为可测试代码

时间:2010-12-23 16:00:39

标签: c# unit-testing

我已经阅读了很多地方,如果你的代码不可测试,那意味着代码写得不好。所以这让我开始编写一个可测试的代码并开始使用一些单元测试框架。

有了这个,虽然我开始寻找一些不可测试的代码并逐渐转换为可测试代码的示例。我发现了很多关于单元测试的例子,但是如果有人可以提供上面的例子,它可能会为我开始。

TIA

4 个答案:

答案 0 :(得分:7)

以下是两本可以帮助您入门的好书:

  1. The Art of Unit Testing
  2. Working Effectively with Legacy Code
  3. 祝你好运。

答案 1 :(得分:1)

我完全不同意。例如,假设您有一个基于计时器执行操作的程序。如果您想以确定的方式测试它,则必须更改并暂停系统时钟。有大量关于定时测试的确定性测试的文献。所以一切都是可测试的,问题是,多么容易。并且它不依赖于代码的编写程度,它取决于程序的实际任务以及如何设计,而不是实现。即使设计良好,您也可以获得难以测试的代码。

答案 2 :(得分:1)

放一堆code in a button click event并尝试对其进行单元测试。这不是不可能,但要么是非平凡的,要么需要一些复制粘贴才能完成它。

protected void buttonClick(object sender, EventArgs e)
{
    string currUser =
        User.Identity.Name.ToString().Trim()
            .Substring(User.Identity.Name.ToString().Trim()
            .IndexOf("\\") + 1);

    Inventory.Employee.DB objEmpDB = new Inventory.Employee.DB();
    Inventory.Employee.Details objEmpDetails = 
        new Inventory.Employee.Details();

    objEmpDetails = objEmpDB.Get(currUser);

    Welcome.Text = 
        "Current User: " + objEmpDetails.Employee_Full_Name;

    var objUserDetails = new Inventory.User.Details();
    Inventory.User.DB objUserDB = new Inventory.User.DB();

    if (objUserDB.UserAuthenticates(currUser))
    {
        objUserDetails = objUserDB.Get(currUser);
        currUserToken = objUserDetails.User_Token.Value;

        userID.Text = currUser;

        if (objUserDetails.Active_User_Name != objUserDetails.User_Name)
        {
            lShadow.Text = "Showin: " + objUserDetails.Active_User_Name;
            lServer.Text = "(" +
            objUserDB.UserPermissionName(objUserDetails.Active_Logon_Name)
                + ") - " + System.Environment.MachineName;
            lShadow.ToolTip = Inventory.Properties.Settings.Default
                .connectionString.Substring(0, Inventory.Properties
                .Settings.Default.connectionString.IndexOf(';'));
            divShadow.Visible = true;
        }
        else
            divShadow.Visible = false;

        lWelcome.Text = "Current User: " + objUserDetails.User_Name;
    }
}

由于难以模拟用户按钮点击,不仅难以实现这一点,还要查看该按钮点击的内容。如果您的单元测试失败,那么大约有100个可能出错的东西。 DRY,单一关注点和其他设计原则导致代码易于测试且易于修复。毕竟,如果你测试旅而不是单位,那么单位测试会有什么好处:)

更新:(如何修复上述代码)
我不会假装上面的代码是一个简单的解决方案。这是我过去曾做过的代码库中的“小”样本。我想表明现实生活中有多糟糕。

代码存在两个主要问题。

  1. 很难测试按钮点击 事件。
  2. 太过分了 用一种方法。
  3. 很容易修复事件驱动/再现按钮点击事件的问题。您可以将所有代码包装到另一个方法中:

    protected void buttonClick(object sender, EventArgs e)
    {
       EasyToCallMethod();
    }
    
    public void EasyToCallMethod()
    {
        string currUser =
            User.Identity.Name.ToString().Trim()
            .Substring(User.Identity.Name.ToString().Trim().IndexOf("\\") + 1);
        //...rest of code
    }
    

    现在很容易通过单元测试来调用。但是,这有点傻,因为它确实无法解决第二个问题。

    轻松修复
    因此,我们可以通过这一个方法调用进行良好的15-20个测试。只需对具有特定目的的每一行进行测试(比如在进行方法调用的地方),你应该有足够小的单元测试来分辨出哪些东西坏了,并且代码覆盖率很高。
    高级内容
    还有很多工作要做。我们可以实现n层MVC或MVVM。在某些时候,你必须问自己是否过度工程。单元测试应该使您的代码更易于维护,但不要过度抽象自己到虚无。这是您自己的风格和体验发挥作用的地方。如果您觉得自己已经掌握了基础知识,那么您应该回答问题,或者找一本好书。

答案 3 :(得分:1)

使代码更多(单元)可测试的最重要的关键因素是依赖注入。

Mark Seemann的书的第一章

  

.NET中的依赖注入

可在此免费获取http://www.manning.com/seemann/ PDF文件。它包含一个非常完整的示例,如何通过减少较低层的依赖性来使一段紧密耦合的代码更易于测试。