自动测试属性getter / setter

时间:2011-08-22 11:57:04

标签: c# unit-testing properties

我们在域对象的许多属性上使用支持字段,例如:

protected string _firstname;

public virtual string Firstname
{
    get { return _firstname; }
    set { _firstname = value; }
}

我偶尔会像下面的例子那样做出愚蠢的拼写错误,并希望编写一个验证所有这些属性的测试,而不是手动对每个对象进行测试。

public virtual string Firstname
{
    get { return _firstname; }
    set { _firstname = Firstname; }
}

是否容易编写或者是否已存在库来测试这些支持字段是否正确获取/设置?这只会在具有setter的属性上运行,并且(可能)是使用camel-case下划线匹配属性名称的后备字段

3 个答案:

答案 0 :(得分:5)

另一种解决方案是使用自动属性来消除此问题:

public virtual string FirstName { get; set; }

更新(参见评论,支持字段似乎需要): 另一种可能性是产生pocos。简单的t4模板'Person.tt'

<#@ template language="C#" #>
<# var pocos = new [] {
    Tuple.Create("FirstName", "string"), 
    Tuple.Create("LastName", "string"), 
    Tuple.Create("Age", "int")}; #>
public partial class Person {
    <# foreach(var t in pocos) {#>

    protected <#= t.Item2#> _<#= t.Item1.ToLowerInvariant()#>;
    public virtual <#= t.Item2#> <#= t.Item1#>
    {
        get { return _<#= t.Item1.ToLowerInvariant()#>; }
        set { _<#= t.Item1.ToLowerInvariant()#> = value; }
    }
    <#}#>
}

现在这当然可以带来尽可能多的问题,但它可能值得一看......也许:)

答案 1 :(得分:2)

除了使用自动属性之外,我会考虑使用反射来测试我的模型..

只需编写一个获取类的所有属性的泛型方法,然后使用以下方法:

/ get value of property: public double Number
double value = (double)numberPropertyInfo.GetValue(calcInstance, null);

[C#]
// set value of property: public double Number
numberPropertyInfo.SetValue(calcInstance, 10.0, null);

对于你的例子:

void Main()
{
        const int testValue=5;
    var test = (Test)Activator.CreateInstance(typeof(Test));
    PropertyInfo valuePropertyInfo = typeof(Test).GetProperty("Value");
    valuePropertyInfo.SetValue(test, testValue, null);
    int value = (int)valuePropertyInfo.GetValue(test, null);
    Console.Write(value);   //Assert here instead

}
public class Test
{
private int _value;
public int Value {get {return _value;}  set{_value=Value;}}
}

上述函数的输出为0而不是预期的5。在这里断言会引发错误。

您如何看待这种方法。

答案 2 :(得分:1)

Gallio/MbUnit有一个contract verifier,它可以完全满足您的要求。 AccessContract的典型用法如下:

public class Foo // Dummy reference type.
{
  private readonly int value;
  public int Value { get { return value; } }

  public Foo (int value)
  {
    this.value = value;
  }
}

public class Bar
{
  private Foo foo;

  public Bar(string unusedParameter) { }

  public Foo Foo // A complex property to be tested!
  {
    get { return foo; }
    set
    {
      if (value == null)
        throw new ArgumentNullException("value");
      if (value.Value < 0)
        throw new ArgumentOutOfRangeException();
      if (value.Value == 666)
        throw new ArgumentException("Inferno value strictly forbidden.");

      foo = value;
    }
  }
}

使用AccessorContract对该属性进行各种测试的测试夹具。

[TestFixture]
public class BarTest
{
  [VerifyContract]
  public readonly IContract AccessorTests = new AccessorContract<Bar, Foo>
  {
      Getter = target => target.Foo,
      Setter = (target, value) => target.Foo = value,
      ValidValues = { new Foo(123), new Foo(456), new Foo(789) },
      AcceptNullValue = false,
      DefaultInstance = () => new Bar("Hello"),
      InvalidValues =
      {
          { typeof(ArgumentOutOfRangeException), new Foo(-123), new Foo(-456) },
          { typeof(ArgumentException), new Foo(666) }
      }
  };
}

合同验证者生成以下单元测试:

enter image description here

有关更多用法示例,请查看MbUnit test project