单元测试示例?

时间:2011-02-05 22:34:06

标签: unit-testing language-agnostic

我理解测试驱动开发背后的想法,首先编写测试,代码反对测试,直到它成功。它还没有在我的工作流程中为我而来。

您能举例说明单元测试可用于前端或后端Web开发环境吗?

1 个答案:

答案 0 :(得分:67)

你没有指定一种语言,所以我会尝试保持这种通用性。但这很难,因为用实际代码表达概念要容易得多。

单元测试起初可能有点令人困惑。有时,并不总是清楚如何测试某些东西,或测试的目的是什么。

我喜欢将单元测试视为测试小块代码的一种方法。

我使用单元测试的第一个地方是验证某些方法的工作方式与我期望的所有情况一样。我刚刚为我的网站编写了一个电话号码验证方法。我接受任何输入,从123-555-1212,(123)555-1212等。我想确保我的验证方法适用于所有可能的格式。如果没有单元测试,我将被迫手动输入每种不同的格式,并检查表单是否正确发布。这非常乏味且容易出错。稍后,如果有人对手机验证码进行了更改,那么如果我们可以轻松检查以确保没有其他任何损坏,那就太好了。 (也许我们增加了对国家代码的支持)。所以,这是一个简单的例子:

public class PhoneValidator
{
    public bool IsValid(string phone)
    {
          return UseSomeRegExToTestPhone(phone);
    }
}

我可以写一个像这样的单元测试:

public void TestPhoneValidator()
{
     string goodPhone = "(123) 555-1212";
     string badPhone = "555 12"

     PhoneValidator validator = new PhoneValidator();

     Assert.IsTrue(validator.IsValid(goodPhone));
     Assert.IsFalse(validator.IsValid(badPhone));
}

这2个Assert行将验证从IsValid()返回的值分别为true和false。

在现实世界中,你可能会有很多好的和坏的电话号码的例子。我测试了大约30个电话号码。以后简单地运行此单元测试将告诉您手机验证逻辑是否已损坏。

我们还可以使用单元测试来模拟我们无法控制的事物。

单元测试应独立于任何外部资源运行。您的测试不应该依赖于存在的数据库或可用的Web服务。因此,我们模拟这些资源,因此我们可以控制它们返回的内容。例如,在我的应用程序中,我无法在注册时模拟被拒绝的信用卡。银行可能不希望我提交数以千计的不良信用卡只是为了确保我的错误处理代码是正确的。这是一些示例代码:

public class AccountServices
{
   private IBankWebService _webService = new BankWebService();

   public string RegisterUser(string username, string creditCard)
   {
        AddUserToDatabase(username);

        bool success = _webService.BillUser(creditCard);

        if (success == false)
           return "Your credit card was declined"
        else
           return "Success!"

    }
}

这是单元测试非常混乱且不明显的地方。这种方法的测试应该怎样做?首先,如果我们检查一下,如果计费失败,则会返回相应的错误消息,这将是非常好的。事实证明,通过使用模拟,有一种方法。我们使用所谓的控制反转。现在,AccountServices()负责创建BankWebService对象。让我们让这个类的调用者提供它:

public class AccountServices
{
   public AccountServices(IBankWebService webService)
   {
       _webService = webService;
   }

   private IBankWebService _webService;

   public string RegisterUser(string username, string creditCard)
   {
        AddUserToDatabase(username);

        bool success = _webService.BillUser(creditCard);

        if (success == false)
             return "Your credit card was declined"
        else
           return "Success!"

    }
}  

因为调用者负责创建BankWebService对象,所以我们的Unit测试可以创建一个假的:

public class FakeBankWebService : IBankWebService
{
    public bool BillUser(string creditCard)
    {
        return false; // our fake object always says billing failed
    }
}

public void TestUserIsRemoved()
{
    IBankWebService fakeBank = FakeBankWebService();

    AccountServices services = new AccountServices(fakeBank);

    string registrationResult = services.RegisterUser("test_username");

    Assert.AreEqual("Your credit card was declined", registrationResult);
}

通过使用该假对象,无论何时调用我们银行的BillUser(),我们的假对象将始终返回false。我们的单元测试现在验证如果对银行的调用失败,RegisterUser()将返回正确的错误消息。

假设有一天你正在进行一些更改,并且有一个错误:

public string RegisterUser(string username, string creditCard)
{
    AddUserToDatabase(username);

    bool success = _webService.BillUser(creditCard);

    if (success) // IT'S BACKWARDS NOW
        return "Your credit card was declined"
    else
        return "Success!"

}

现在,当您的结算失败时,您的RegisterUser()方法会返回“Success!”。幸运的是,你有一个单元测试。该单元测试现在将失败,因为它不再返回“您的信用卡被拒绝”。

以这种方式查找错误比使用不良信用卡手动填写注册表更容易,更快,只是为了检查错误消息。

一旦你看到不同的模拟框架,你就可以做更强大的事情。您可以验证是否调用了伪方法,可以验证调用方法的次数,可以验证调用方法的参数等等。

我认为一旦你理解了这两个想法,你就会理解为你的项目编写大量的单元测试。

如果您告诉我们您正在使用的语言,我们可以更好地指导您。

我希望这会有所帮助。如果其中一些令人困惑,我道歉。如果事情没有意义,我会清理它。