单元测试c#属性

单元测试c#属性,c#,.net,tdd,xunit,C#,.net,Tdd,Xunit,我和一个有很多属性的类一起工作。比如, public class Bib { public int PartQty { get; set; } } 现在进行单元测试;我做了xUnit测试,比如 [Fact] public void CanGetAndSetPartQuantity() { const int expected = 3; var target = new Bib() {PartQty = expec

我和一个有很多属性的类一起工作。比如,

public class Bib
{        
    public int PartQty { get; set; }
}
现在进行单元测试;我做了xUnit测试,比如

    [Fact]
    public void CanGetAndSetPartQuantity()
    {
        const int expected = 3;

        var target = new Bib() {PartQty = expected};

        Assert.Equal(expected, target.PartQty);
    }

在这里,我讨厌我如何硬编码expected=3。对于accessor和mutator,测试此属性的好方法是什么?

由于此属性除了作为整数的getter/setter之外没有其他行为,因此您实际上只是测试编译器是否工作。这基本上不会给测试增加任何价值。考虑完全移除它。那会使你摆脱这种奇怪的处境


如果您确实有一些您试图捕获的行为(例如,允许的边界条件),您只需要测试这些行为,实际上不需要其他。通常,这些边界条件的常数作为对象的一部分可用。考虑使用这些常量,+/一些适当的增量。

< P>我坚信单元测试是“白盒”测试,这意味着你可以使用已知的角落情况来选择你的测试输入。在这种情况下,如果使用auto属性,那么如果您信任您的编译器,那么测试是不必要的。如果您不能信任编译器以您期望的方式实现自动属性,那么您也不能信任它以您编写的方式执行测试

也就是说,如果您有一个更复杂的setter,您将根据可能的故障情况选择输入。几个典型案例:

  • 验证>=0的属性的负数
  • 其他验证失败
  • 极端的边界情况,如Int.MaxValue,有时会触发setter中的溢出和意外行为
  • 应该通过验证的任意值(这里没有关于如何选择值的实际指导,只要您知道它在您的“好”情况下。)
    • 这应该有助于

      [Fact]     
      public void CanGetAndSetPartQuantity()     
      {
          bool fail = false;
          int expected = 0;
      
          while (!fail && expected < int.MaxValue)
          {
              var target = new Bib() {PartQty = expected};          
              fail = expected != target.PartQty;
              expected++;
          }
      
          Assert.IsTrue(!fail);
      } 
      
      [事实]
      public void CanGetAndSetPartQuantity()
      {
      布尔失败=错误;
      int预期为0;
      而(!fail&&expected
      非常适合这种单元测试。改为这样写:

      [Fact]
      public void CanGetAndSetPartQuantity()
      {
          const int expected = new Random().Next();
      
          var target = new Bib() {PartQty = expected};
      
          Assert.Equal(expected, target.PartQty);
      }
      

      这确保了无论输入是什么,输出都能正确地表示输入。

      我刚才听了一篇关于良好单元测试实践的演讲(很抱歉,这个家伙的名字逃过了我脆弱的记忆)。他提倡使用精心挑选的名称在常量中存储这样的值

      在你的情况下,我会用这样的名字

      const int SomeRandomValidPartQuantity=3;
      

      通过这种方式,您表示要准确使用此值,在这种情况下,您只是在寻找任何有效数量

      测试应该来自某种用例。有趣的是,首先你介绍了你的类,然后谈到编写一个测试,这是向后TDD的

      用例通知测试,测试通知代码。我非常怀疑您的用例是“我的API的用户可以将名为
      PartQty
      的属性设置为任何整数,并始终返回他们设置的整数”。如果这是真正的用例,您应该编写一个单元测试来检查
      int.MaxValue
      int.MinValue
      。然而,这些很少是现实世界的价值观

      一个真实的用例可能会是这样的:“我的API的用户向
      Bib
      注入
      IFlugleBinder
      ,将
      PartQty
      设置为4,然后调用
      Execute
      方法。这会在
      IFlugleBinder
      实例上调用
      Bind
      方法4次。”如果这是用例,你的测试看起来会很不一样

      老实说,它看起来像是某种DTO。根据我的经验,大多数DTO只是一些更高级别用例的产物。如果DTO是作为API提供的函数调用的结果返回的,那么您实际上应该返回一个接口,DTO类本身应该是私有的,在这种情况下,不需要显式地测试它(只需测试从方法调用获得的实际结果的属性)。同样,如果它是一个从未公开的内部DTO,那么就不要公开它。如果您的用户必须提供一些值包,那么您的API应该接受一个接口。让用户定义自己实现接口的类,或提供一个不可变的类,如下所示:

      public class Bib : IBib
      {
          public Bib(int partQty)
          {
              PartQty = partQty;
          }
          public int PartQty { get; private set; }
      }
      
              [Theory]
              [AutoData]
              public void CanGetAndSetPartQuantity(int expected)
              {
                  var target = new Bib() {PartQty = expected};
      
                  Assert.Equal(expected, target.PartQty);
              }
      

      然后你可以编写一个测试来检查你的构造函数是否工作,如果你想学究的话,但这并不是那么重要。

      虽然我也认为这属于“测试到无聊”类别,但如果你确实觉得这是值得测试的,批准测试提供了一种非常简单的测试方法。附件是一个检查属性的简单测试

      [TestMethod]
      [UseReporter(typeof(DiffReporter))]
      public void TestMethod1()
      {
          var fred = new Person{
                  Age = 35,
              FirstName = "fred",
              LastName = "Flintstone",
              Hair = Color.Black
                 };
          Approvals.Verify(fred.WritePropertiesToString());
      }
      
      这将生成一个文件,其内容如下:

      Person
      {
          Age: 35
          FirstName: fred
          LastName: Flintstone
          Hair: Color [Black]
      }
      
      只需将该文件重命名为.approved即可

      请注意扩展方法的使用:.WritePropertiesToString()

      这里有一段视频介绍了认证测试的基本知识

      MsTest:

      努尼特:


      Xunit:

      您还可以像这样使用autofixture autodata属性:

      public class Bib : IBib
      {
          public Bib(int partQty)
          {
              PartQty = partQty;
          }
          public int PartQty { get; private set; }
      }
      
              [Theory]
              [AutoData]
              public void CanGetAndSetPartQuantity(int expected)
              {
                  var target = new Bib() {PartQty = expected};
      
                  Assert.Equal(expected, target.PartQty);
              }
      

      您可能使用了一个“神奇数字”,但至少您在测试中只声明了一次-因此我很难看出问题所在。您是否希望.NET Framework停止工作?我不建议为自动属性编写测试。与这里的公众观点相反,在生成库时,这种测试是非常有效的。与其说是成功,不如说是编译时没有错误。单元测试执行这两个功能。即使没有单元测试,它也会编译无误,前提是该属性曾经被使用过。如果没有使用它,那么它就不应该存在。在生成库时,这种测试是非常有效的,因为您可能会在稍后的某个时间点决定为属性使用更复杂的getter或setter逻辑,并且该测试将确保在该时间到来时不会破坏任何东西。我假设这是一个