【问题标题】:When to use a unit testing framework (vs. just using asserts)?何时使用单元测试框架(与仅使用断言相比)?
【发布时间】:2011-01-11 21:52:09
【问题描述】:

我正在使用一个小型(目前为 150 位置,完成后可能少于 500)C 项目,我正在自学测试驱动开发。根据我在网上找到的一些东西——尤其是these slides by Olve Maudal,我刚刚在我的单元测试中使用了断言。

由于我只是在学习 tdd,因此到目前为止我已经避免了学习单元测试框架(例如 cunit)的开销。在这一点上,我的想法是,框架的额外学习曲线——即使很浅——不值得为这样一个小项目付出努力。我想知道:这是不正确的(即我现在最好学习框架)?学习框架在什么时候有回报?

【问题讨论】:

    标签: unit-testing tdd


    【解决方案1】:

    学习单元测试框架的收支平衡并不遥远。与使用 assert() 函数相比,使用框架获得的两个主要优势是

    • 更详细的断言失败消息:预期值与实际值
    • 执行所有测试用例代替在第一个失败的断言时停止

    这两个给出了错误所在位置的提示。

    【讨论】:

      【解决方案2】:

      根据我学习 .NET 单元测试框架的经验,我几乎从一个小项目开始,然后让我能够在以后将我学到的知识应用到其他项目中,而不管它们的大小。

      我猜你可能在想有什么意义,现在只是一小段代码,但我认为你最好现在而不是以后进入一个框架。这有点像开车——我在 17 岁时学会了开车,但直到两年后我才拥有自己的汽车。在我拿到车之前,我本可以忽略上课,但我通过了并且已经具备继续开车的技能。

      有点奇怪的类比,但这是我在学习框架或库之类的东西时的想法 - 你永远不知道它会对你有用。

      【讨论】:

        【解决方案3】:

        学习 xUnit 框架 - 当您开始编写更大规模的商业项目时,您会发现它们被广泛使用。

        【讨论】:

        • 感谢您的回复。我的大部分工作都是在科学环境中进行的,并且倾向于相对较小的项目。但是,我倾向于使用 xunit 框架,特别是因为我在切换语言时可能会使用其他框架。
        【解决方案4】:

        由于我只是在学习 tdd,因此到目前为止我已经避免了学习单元测试框架(例如 cunit)的开销。在这一点上,我的想法是,框架的额外学习曲线——即使很浅——不值得为这样一个小项目付出努力。我想知道:这是不正确的(即我现在最好学习框架)?学习框架在什么时候有回报?

        我同意 philippe、Jason 和 Paolo 的观点,但我想换一种看法:

        所谓的xUnit 框架是一个非常简单和轻量级的,既可以作为库下载(例如最新的 JUnit 或 CUnit 的二进制文件大约 200 KB),更重要的是,概念上。

        如果您正在(或想要)进行任何单元测试,您肯定会从使用经过验证的工具中受益,并且学习 xUnit 将立即开始获得回报。我认为你可能高估了所涉及的“开销”——不要让“框架”这个词吓到你;-)——你将在半小时或更短的时间内启动并运行 xUnit。

        免责声明:您的问题未标记为 C,因此我将其视为与语言无关的问题。我不知道 C 端的单元测试是否有很大不同(与 Java 相比),但我对此表示怀疑。 :-)

        【讨论】:

          【解决方案5】:

          以下是一个 C# .Net 示例,其中 Moq framework 在使用 Visual Studio 的 MSTest 中很有用。

          观察TestOld()MoqTest() 之间的区别。两个测试的目的是一样的。但是在TestOld(),我需要写MyMemberManagerForTest的具体实现。而在MoqTest() 中,Moq 框架将为我完成任务。

            _mockIMemberManager = new Mock<IMemberManager>();
            _mockIMemberManager.Setup(x => x.GetMember(It.IsAny<int>())).Returns(member);
          

          参考:How to Use Mock Library For Your Unit Testing In C#

          [TestClass()]
          public class TestMembershipManager
          {
              private LibraryCore _targetLibraryCore;
          
              #region Old Test withou Moq
          
              public class MyMemberManagerForTest : IMemberManager
              {
                  public Member GetMember(int memberID)
                  {
                      Member member = new Member()
                      {
                          MemberID = 1,
                          FirstName = "Lijo",
                          MaximumBookCanBorrow = 4,
                      };
          
                      return member;
                  }
              }
          
              private IMemberManager _memberManager;
          
              [TestMethod()]
              public void TestOld()
              {
                  _memberManager = new MyMemberManagerForTest();
                  _targetLibraryCore = new LibraryCore(_memberManager);
          
                  int memberID = 1;
                  double expected = 12;
                  double actual;
          
                  //TEST
                  actual = _targetLibraryCore.CalculateMembershipCost(memberID);
                  Assert.AreEqual(expected, actual);
              }
          
          
              #endregion
          
              #region Test with Moq
              private Mock<IMemberManager> _mockIMemberManager;
          
              [TestMethod()]
              public void MoqTest()
              {
                  _mockIMemberManager = new Mock<IMemberManager>();
                  _targetLibraryCore = new LibraryCore(_mockIMemberManager.Object);
          
                  Member member = new Member()
                  {
                      MemberID = 1,
                      FirstName = "Lijo",
                      MaximumBookCanBorrow = 4,
                  };
          
                  _mockIMemberManager.Setup(x => x.GetMember(It.IsAny<int>())).Returns(member);
          
                  int memberID = 1;
                  double expected = 12;
                  double actual;
          
                  //TEST
                  actual = _targetLibraryCore.CalculateMembershipCost(memberID);
                  Assert.AreEqual(expected, actual);
              }
          
              #endregion
          
          }
          

          如您在重载的 LibraryCore 构造函数中所见,代码使用Dependency Injection 非常重要

          public class LibraryCore
          {
              private readonly IMemberManager _memberManager;
          
              public LibraryCore(IMemberManager memberManager)
              {
                  this._memberManager = memberManager;
              }
          
              public double CalculateMembershipCost(int memberID)
              {
                  double membershipCost = 0;
                  Member member = _memberManager.GetMember(memberID);
                  membershipCost = 10 + member.MaximumBookCanBorrow * 0.5;
                  return membershipCost;
              }
          }
          

          【讨论】:

            【解决方案6】:

            注意:如果您遵循红-绿-重构的正常模式,那么当您重构单元测试时,生成的代码应该演变为单元测试框架。例如,你不应该有两个代码副本

            if (expected != actual) {printf("%d != %d", actual, expected); return -1;}
            

            这应该被重构为类似的东西

            void AssertNotEqual(int expected, int actual, char* message)
            {
                if (expected != actual) {printf(message, actual, expected); return -1;}
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-06-17
              • 1970-01-01
              • 2020-07-25
              • 2014-08-12
              • 2019-05-21
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多