在编写单元测试时,您如何知道要测试什么?

使用 C # ,我需要一个名为 User的类,它具有用户名、密码、活动标志、名、姓、全名等。

应该有方法来 鉴定拯救一个用户。我只是为这些方法编写一个测试吗?我是否需要担心测试性质,因为它们是。Net 的接球手和接球手?

85673 次浏览

我不能确切地说是 C # ,但是当我编写单元测试时,我会测试每一个输入,即使是那些用户没有做的,这样我就知道如何防止我自己的错误。

规则是你必须测试你写的每一个逻辑。如果您在 getter 和 setter 中实现了一些特定的功能,我认为它们值得测试。如果他们只为某些私有字段赋值,那就不必麻烦了。

就我对敏捷开发背景下的单元测试的理解而言,Mike,是的,您需要测试 getter 和 setter (假设它们是公开可见的)。单元测试的整个概念是将软件单元(在本例中是一个类)作为 黑匣子进行测试。由于 getter 和 setter 在外部是可见的,所以需要将它们与 Authenticate 和 Save 一起测试。

为 getter 和 setter 编写单元测试没有坏处。现在,他们可能只是私下进行字段获取/设置,但是将来您可能需要测试验证逻辑或属性间的依赖关系。当你在思考的时候写出来比较容易,如果那个时候到来的话记得改进它。

如果 Authenticate 和 Save 方法使用这些属性,那么测试将间接触及这些属性。只要这些属性只是提供对数据的访问,那么就不需要进行显式测试(除非您要达到100% 的覆盖率)。

测试代码,而不是语言。

单元测试如下:

Integer i = new Integer(7);
assert (i.instanceOf(integer));

只有在您正在编写编译器并且 instanceof方法不工作的可能性为非零的情况下才有用。

不要测试那些你可以依靠语言来强制执行的东西。在您的情况下,我将重点关注您的身份验证和保存方法——并且我将编写测试,以确保它们能够优雅地处理任何或所有这些字段中的 null 值。

我会测试你的读取器和设置器。根据编写代码的人,有些人会更改 getter/setter 方法的含义。我已经看到变量初始化和其他验证作为 getter 方法的一部分。为了测试这类事情,您需要显式地进行覆盖该代码的单元测试。

就个人而言,我会“测试任何可以打破”和简单的 getter (甚至更好的汽车属性)将不会打破。我从来没有一个简单的 return 语句失败过,因此从来没有对它们进行过测试。如果 getter 中包含计算或其他形式的语句,我当然会为它们添加测试。

我个人使用 莫克作为一个模拟对象框架,然后验证我的对象是否按照它应该的方式调用周围的对象。

您必须用 UT 覆盖类的每个方法的执行并检查方法返回值。这包括 getter 和 setter,特别是在成员(属性)是复杂类的情况下,这需要在初始化过程中分配大量内存。例如,用一些非常大的字符串(或者希腊符号)调用 setter,并检查结果是否正确(没有截断,编码是正确的等等)

对于同样适用的简单整数,如果传递 long 而不传递整数,会发生什么情况?这就是你写 UT 的原因:)

如果它们真的是琐碎的,那么就不要费心去测试了

public class User
{
public string Username { get; set; }
public string Password { get; set; }
}

另一方面,如果您正在做一些聪明的事情(比如在 getter/setter 中加密和解密密码) ,那么对它进行测试。

类的测试应该验证:

  1. 方法和属性返回期望值
  2. 当提供无效参数时,将引发适当的异常
  3. 当调用给定方法时,类和其他对象之间的交互按预期发生

当然,如果 getter 和 setter 没有特殊的逻辑,那么 Authenticate 和 Save 方法的测试应该覆盖它们,但是否则应该编写一个显式的测试

我不会测试属性的实际设置。我更关心的是这些属性是如何被消费者填充的,以及他们用什么来填充它们。对于任何测试,您都必须将风险与测试的时间/成本进行权衡。

您应该尽可能使用单元测试来测试“每个非平凡的代码块”。

如果您的属性是微不足道的,而且不太可能有人会在其中引入 bug,那么不对它们进行单元测试应该是安全的。

Authenticate ()和 Save ()方法看起来很适合测试。

测试样板代码是浪费时间,但是正如 Slavo 所说,如果给 getter/setter 添加了副作用,那么您应该编写一个测试来配合该功能。

如果你正在做测试驱动开发,你应该首先编写契约(例如接口) ,然后编写测试来实践那个记录预期结果/行为的接口。然后可以自己编写方法,而不需要修改单元测试中的代码。最后,获取一个代码覆盖工具,并确保测试运行代码中的所有逻辑路径。

理想情况下,您应该在编写类时完成单元测试。这就是您在使用测试驱动开发时应该做的事情。在实现每个功能点时添加测试,确保也用测试覆盖边界情况。

之后编写测试要痛苦得多,但是是可行的。

如果我是你,我会这么做:

  1. 编写测试核心函数的基本测试集。
  2. 获取 NCover 并在测试中运行它。此时测试覆盖率可能在50% 左右。
  3. 继续添加覆盖边界情况的测试,直到覆盖率达到80% -90% 左右

这将为您提供一组良好的工作单元测试,它们将充当针对回归的良好缓冲区。

这种方法的唯一问题是代码必须是 设计的才能以这种方式进行测试。如果您在早期犯了任何耦合错误,那么您将不能很容易地获得高覆盖率。

这就是为什么在编写代码之前编写测试非常重要的原因。它强制您编写松散耦合的代码。

不要测试明显可用的代码(样板代码)。因此,如果您的 setter 和 getter 只是“ properties value = value”和“ return property tyvalue”,那么测试它是没有意义的。

甚至 get/set 也可能产生奇怪的结果,这取决于它们是如何实现的,因此应该将它们视为方法。

每个测试都需要为属性指定一组参数,定义可接受和不可接受的属性,以确保调用以预期的方式返回/失败。

您还需要了解安全隐患,例如 SQL 注入,并对其进行测试。

所以,是的,您确实需要担心测试属性。

真正琐碎的代码,比如 getter 和 setter,如果没有比设置私有字段更多的行为,那么对于测试来说就有些过头了。在3.0中,C # 甚至有一些语法上的优点,编译器会处理私有字段,所以你不必编程。

我通常编写许多非常简单的测试来验证我期望从我的类中得到的行为。即使是像加两个数这样简单的事情。我在编写一个简单的测试和编写一些代码行之间进行了很多切换。这样做的原因是,我可以改变代码,而不用担心我破坏了我没有想到的东西。

一般来说,当一个方法只为某些值定义时,测试 一次又一次上可接受值的边界。换句话说,确保您的方法做了它应该做的事情,仅此而已。这很重要,因为当你要失败的时候,你希望尽早失败。

在继承层次结构中,确保测试 LSP遵从性。

测试默认的 getter 和 setter 对我来说似乎没有多大用处,除非您计划稍后进行一些验证。

我认为测试 getters 和 setters 是愚蠢的,因为它们只做一个简单的操作。就个人而言,我不会编写复杂的单元测试来覆盖任何使用模式。我尝试编写足够多的测试,以确保我已经处理了正常的执行行为和我能想到的所有错误情况。我将编写更多的单元测试作为对 bug 报告的响应。我使用单元测试来确保代码满足需求,并使将来的修改更加容易。当我知道如果我破坏了某些东西,测试就会失败时,我会更愿意改变代码。

许多伟大的回答这也是关于我的问题: “ 开始 TDD-挑战? 解决方案? 建议?

我也可以推荐你看一下我的 博客文章(它的灵感部分来自于我的问题) ,我已经得到了一些很好的反馈。即:

我不知道从何说起?

  • 重新开始。只有在你写新东西的时候才考虑写测试 代码。这可以重新工作的旧 或者一个全新的特性。
  • 从简单的开始,不要跑开,试图让你的头脑清醒 一个测试框架 TDD 式的,调试,断言工作正常。 - 以此为出发点-不是的 搞乱你的项目或创造 依赖关系。
  • 积极的开始。你正在努力提高你的技能,感觉良好 我见过很多开发商 那些乐于停滞不前的国家 不去尝试新事物 你做得对 事情,记住这一点,它会有所帮助 阻止你放弃。
  • 开始准备一个挑战。这是很难开始进入 测试。期待一个挑战,但是 记住,挑战是可以克服的。

只测试你所期望的

我第一次遇到麻烦的时候 因为我经常坐着 试图弄清楚每一个 可能出现的问题 然后试着测试并修复。 这是解决头痛的快速方法。 测试应该是一个真正的 YAGNI 过程。如果你知道有一个 然后为它编写一个测试。 否则,不用麻烦了。

只测试一件事

每个测试用例应该只测试 有一件事,如果你发现自己 在测试用例名中加上“ and” 你做错了。

我希望这意味着我们可以从“ getters 和 setter”继续:)

我会编写一个测试,测试您正在为 GUI 接口之外的可测试代码编写的任何东西。

通常,我编写的任何逻辑都具有放置在另一层或业务逻辑层中的任何业务逻辑。

然后,为任何执行某些操作的东西编写测试是很容易的。

首先,为“业务逻辑层”中的每个公共方法编写一个单元测试。

如果我有这样的课程:

   public class AccountService
{
public void DebitAccount(int accountNumber, double amount)
{


}


public void CreditAccount(int accountNumber, double amount)
{


}


public void CloseAccount(int accountNumber)
{


}
}

在编写任何代码之前,我要做的第一件事就是开始编写单元测试,因为我知道我要执行这些操作。

   [TestFixture]
public class AccountServiceTests
{
[Test]
public void DebitAccountTest()
{


}


[Test]
public void CreditAccountTest()
{


}


[Test]
public void CloseAccountTest()
{


}
}

编写测试来验证所编写的用于执行某些操作的代码。如果您迭代一个事物集合,并改变每个事物的某些内容,那么编写一个执行相同操作的测试,并断言实际发生的情况。

您可以采用许多其他方法,即 Behavor Driven Development (BDD) ,它涉及的内容更多,而且不适合从单元测试技能开始。

所以,这个故事的寓意是,测试任何你可能担心的东西,保持单元测试测试特定的小尺寸的东西,很多测试都是好的。

将您的业务逻辑保留在用户界面层之外,这样您就可以轻松地为它们编写测试,这样您就可以做得很好。

我建议使用 TestDriven. Net锐利者,因为它们都可以很容易地集成到 VisualStudio 中。

如果你认为它可以打破,为它写一个测试。我通常不测试 setter/getter,但是让我们假设您为 User 制作了一个。名字,连接名和姓,我会写一个测试,这样如果有人改变了姓和名的顺序,至少他会知道他改变了一些被测试的东西。

规范的回答是“测试任何可能破碎的东西”如果确保属性不会中断,则不要测试它们。

一旦发现有东西坏了(你发现了一个 bug) ,很明显,这意味着你需要对它进行测试。编写一个测试来重现 bug,观察它是否失败,然后修复 bug,然后观察测试是否通过。

你应该测试所有的东西。现在您已经有了 getter 和 setter,但是有一天您可能会稍微改变它们,可能是为了进行验证或其他操作。您今天编写的测试将在明天使用,以确保一切照常运行。 在编写测试时,应该忘记诸如“现在它是微不足道的”之类的考虑事项。在敏捷或测试驱动的环境中,您应该测试假设未来的重构。 另外,您是否尝试过放入非常奇怪的值,比如极长的字符串,或者其他“糟糕”的内容?那么你应该... 永远不要认为你的代码在未来会被滥用得多么严重。

通常,我发现编写大量的用户测试是一方面,令人筋疲力尽。另一方面,尽管它总是给您提供关于应用程序应该如何工作的宝贵见解,并帮助您抛弃简单(和错误)的假设(比如: 用户名的长度总是小于1000个字符)。

我建议为 Authenticate 和 Save 方法编写多个测试。除了成功案例(提供了所有参数,所有内容都拼写正确,等等)之外,还可以对各种失败案例进行测试(不正确或缺少参数,如果适用的话不可用的数据库连接,等等)。我推荐 用 NUnit 进行 C # 语用单元测试作为参考。

正如其他人所说,除非在 getter 和 setter 中有条件逻辑,否则用于 getter 和 setter 的单元测试是多余的。

对于最终可能出现在工具箱或开源项目类型中的简单模块,您应该尽可能多地进行测试,包括琐碎的 getter 和 setter。您需要记住的是,在编写特定模块时生成单元测试是相当简单和直接的。添加 getter 和 setter 是最少的代码,可以不经过深思熟虑就进行处理。但是,一旦您的代码被放置在一个更大的系统中,这种额外的工作可以保护您免受基础系统中的更改(如基类中的类型更改)的影响。测试所有内容是完成回归的最佳方式。

虽然可以正确地猜测代码需要测试的位置,但我通常认为您需要度量来支持这种猜测。在我看来,单元测试与代码覆盖度量密切相关。

具有大量测试但覆盖面较小的代码尚未得到很好的测试。也就是说,覆盖率为100% 但没有测试边界和错误情况的代码也不是很好。

您需要在高覆盖率(最低90%)和可变输入数据之间取得平衡。

记得测试“垃圾在”!

此外,单元测试不是单元测试,除非它检查失败。没有断言或标记为已知异常的单元测试将简单地测试代码在运行时不会死亡!

您需要设计您的测试,以便它们总是报告失败或意外/不需要的数据!

它使我们的代码更好... 句号!

在进行测试驱动开发时,我们软件开发人员忘记的一件事是我们行动背后的目的。如果一个单元测试是在生产代码已经到位之后编写的,那么测试的价值就会大大降低(但并没有完全丢失)。

本着单元测试的真正精神,这些测试是 没有,主要是为了“测试”更多的代码; 或者为了获得90% -100% 更好的代码覆盖率。这些都是首先编写测试的 额外福利。最大的回报是,由于 TDD 的自然过程,我们的生产代码结束编写得更好。

为了更好地传达这一想法,以下内容可能有助于阅读:

单元测试的缺陷理论
有目的的软件开发

如果我们认为编写 更多的单元测试的行为能够帮助我们获得更高质量的产品,那么我们可能正在遭受测试驱动开发的 Cargo Cult

我支持 测试任何可能打破的东西不要写愚蠢的测试。但是最重要的原则是 检测你发现的任何坏东西: 如果某个方法行为异常,编写一个测试来概述使其失败的数据集,然后纠正错误并看着条变绿。还要测试“边界”数据值(null,0,MAX _ INT,空列表,等等)。

在编写单元测试或任何测试时,您通过查看所测试内容的边界条件来确定要测试的内容。例如,您有一个名为 is _ prime 的函数。幸运的是,它完成了它的名称所暗示的任务,并告诉您整数对象是否为质数。为此,我假设您正在使用对象。现在,我们需要检查对于已知范围的素数对象和非素数对象是否出现了有效的结果。那是你的出发点。

基本上,看看在函数、方法、程序或脚本中应该发生什么,然后再看看在相同的代码中 没有应该发生什么。这是你考试的基础。只要准备好修改您的测试,因为您对 应该在代码中发生的事情有了更多的了解。

另一个典型的答案,我相信,来自 Ron Jeffries:

只测试希望工作的代码。

编写没有价值的代码总是一个坏主意。因为建议的测试对您的项目没有任何价值(或者非常接近它)。那么你就是在浪费宝贵的时间,而这些时间本可以用来编写真正带来价值的代码。

这让我进入了单元测试领域,这让我非常高兴

我们刚开始做单元测试。 在很长一段时间里,我知道开始做这件事情是件好事,但是我不知道如何开始,更重要的是我不知道要测试什么。

然后我们不得不重写会计程序中的一段重要代码。 这个部分非常复杂,因为它涉及到许多不同的场景。 我所说的部分是一种支付已经输入到会计系统中的销售和/或购买发票的方法。

我只是不知道如何开始编写代码,因为有这么多不同的支付选项。 发票可能是100美元,但客户只转移了99美元。 也许您已经向某个客户发送了销售发票,但您也从该客户购买了产品。 所以你以300美元把他卖了,但是你却以100美元买下了他。你可以期望你的客户付给你200美元来结清余额。 如果你卖了500美元,但客户只付给你250美元呢?

所以我有一个非常复杂的问题需要解决,有很多种可能性,一个场景可以完美地工作,但是在另一种发票/付款组合中可能是错误的。

这就是单元测试发挥作用的地方。

我开始(在测试代码中)编写一个方法来创建销售和采购发票列表。 然后我写了第二个方法来创建实际的支付。 通常,用户会通过用户界面输入该信息。

然后,我创建了第一个 TestMethod,测试一个非常简单的单张发票支付,没有任何支付折扣。 当银行付款被保存到数据库时,系统中的所有操作都会发生。 如您所见,我创建了一个发票,创建了一个支付(银行交易)并将交易保存到磁盘。 在我的资产,我把什么应该是正确的数字结束在银行交易和链接发票。 我检查付款的数量,付款金额,折扣金额和发票余额后的交易。

测试运行之后,我会进入数据库,再次检查是否存在我所期望的内容。

编写测试之后,我开始编写支付方法(BankHeader 类的一部分)。 在编码中,我只是为了让第一个测试通过而编写代码。我还没有想到其他更复杂的情况。

我运行了第一个测试,修复了一个小 bug,直到测试通过。

然后我开始编写第二个测试,这一次使用了付款折扣。 在编写了测试之后,我修改了支付方法以支持折扣。

在用付款折扣测试正确性的同时,我还测试了简单的付款。 当然,这两项测试都应该通过。

然后我想到了更复杂的情况。

1)设想一个新的场景

2)为那个场景编写一个测试

3)运行那个单独的测试,看看它是否会通过

4)如果没有,我会调试和修改代码,直到它会通过。

5)在修改代码时,我继续运行所有的测试

这就是我如何设法创建我的非常复杂的支付方法。 没有单元测试,我不知道如何开始编码,这个问题似乎是压倒性的。 通过测试,我可以从一个简单的方法开始,并在保证更简单的场景仍然可以工作的情况下逐步扩展它。

我确信使用单元测试可以节省我几天(或几周)的编码时间,并且或多或少地保证了我的方法的正确性。

如果我稍后想到一个新的场景,我可以将它添加到测试中,看看它是否工作。 如果没有,我可以修改代码,但仍然确保其他方案仍然正常工作。 这将在维护和 bug 修复阶段节省大量时间。

是的,即使是测试过的代码,如果用户做了你没有想到或者阻止他做的事情,仍然会有 bug

下面是我为测试我的支付方式而创建的一些测试。

public class TestPayments
{
InvoiceDiaryHeader invoiceHeader = null;
InvoiceDiaryDetail invoiceDetail = null;
BankCashDiaryHeader bankHeader = null;
BankCashDiaryDetail bankDetail = null;






public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
{
......
......
}


public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
{
......
......
......
}




[TestMethod]
public void TestSingleSalesPaymentNoDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 1, "01-09-2008"));
bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
bankHeader.Save();


Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
}


[TestMethod]
public void TestSingleSalesPaymentDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 2, "01-09-2008"));
bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
bankHeader.Save();


Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
}


[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void TestDuplicateInvoiceNumber()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("100", true, 2, "01-09-2008"));
list.Add(CreateSales("200", true, 2, "01-09-2008"));


bankHeader = CreateMultiplePayments(list, 3, 300, 0);
bankHeader.Save();
Assert.Fail("expected an ApplicationException");
}


[TestMethod]
public void TestMultipleSalesPaymentWithPaymentDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 11, "01-09-2008"));
list.Add(CreateSales("400", true, 12, "02-09-2008"));
list.Add(CreateSales("600", true, 13, "03-09-2008"));
list.Add(CreateSales("25,40", true, 14, "04-09-2008"));


bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
bankHeader.Save();


Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);


Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);


Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
}


[TestMethod]
public void TestSettlement()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("300", true, 43, "01-09-2008")); //Sales
list.Add(CreateSales("100", false, 6453, "02-09-2008")); //Purchase


bankHeader = CreateMultiplePayments(list, 22, 200, 0);
bankHeader.Save();


Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
}

我所见过的最好的经验法则就是测试所有你一眼看不出来的东西,确保它们都能正常工作。再多的话,您就要测试语言/环境了。

这个问题似乎是一个如何界定哪些方法需要测试,哪些方法不需要测试的问题。

用于值分配的 setter 和 getter 已经在一致性和未来增长的前提下创建,并且预见到随着时间的推移 setter/getter 可能演变成更复杂的操作。为了保持一致性和未来的发展,对这些方法进行单元测试是有意义的。

代码可靠性,尤其是在进行更改以添加其他功能时,是主要目标。我不知道有人曾经因为在测试方法中包含 setter/getter 而被解雇,但我确信有人希望他们已经测试过的方法,上一次他们知道或者能够回忆起来是简单的 set/get 包装器,但是现在不再是这样了。

也许团队中的另一个成员扩展了 set/get 方法,以包含现在需要测试但之后没有创建测试的逻辑。但是现在您的代码正在调用这些方法,您并没有意识到它们发生了变化,需要进行深入的测试,您在开发和 QA 中进行的测试不会触发缺陷,但是在发布的第一天的真实业务数据确实会触发缺陷。

现在,两个队友将讨论当 set/gets 变形为包含可能失败但没有被单元测试覆盖的逻辑时,是谁放弃了单元测试,以及是谁没有放入单元测试。如果从第一天开始就在简单的 set/gets 上实现测试,那么最初编写 set/gets 的团队成员会更容易完成这些测试。

我的观点是,几分钟的“浪费”时间用单元测试覆盖所有的方法,即使是微不足道的方法,可能会节省几天的头痛和损失金钱/名誉的业务和失去某人的工作。

而且,您确实用单元测试包装了一些琐碎的方法,这一事实可能会被初级团队成员看到,当他们将一些琐碎的方法更改为非琐碎的方法并提示他们更新测试时,现在没有人会遇到麻烦,因为缺陷已经在生产环境中得到了控制。

我们编码的方式,以及从我们的代码中可以看到的规则,可以帮助其他人。