我们在域对象的许多属性上使用支持字段,例如:
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下划线匹配属性名称的后备字段
答案 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) }
}
};
}
合同验证者生成以下单元测试:
有关更多用法示例,请查看MbUnit test project。