Mik*_*att 6 c# unit-testing rhino-mocks c#-4.0
在我的MVC3项目中,我使用IUrlProvider接口来包装UrlHelper类.在我的一个控制器动作中,我有一个这样的调用:
string url = _urlProvider.Action("ValidateCode", new { code = "spam-and-eggs" });
Run Code Online (Sandbox Code Playgroud)
我想在我的单元测试中存根这个方法调用,这是在一个单独的项目中.测试设置看起来像这样:
IUrlProvider urlProvider = MockRepository.GenerateStub<IUrlProvider>();
urlProvider.Stub(u => u.Action(
Arg<string>.Is.Equal("ValidateCode"),
Arg<object>.Is.Equal(new { code = "spam-and-eggs" }) ))
.Return("http://www.mysite.com/validate/spam-and-eggs");
Run Code Online (Sandbox Code Playgroud)
不幸的是,Arg<object>.Is.Equal(new { code = "spam-and-eggs" })不起作用,因为new { code = "spam-and-eggs" } != new { code = "spam-and-eggs" }匿名类型在不同的程序集中声明.
那么,是否有一种替代语法可以与Rhino Mocks一起使用来检查跨程序集的匿名对象之间的匹配字段值?
或者我应该用类替换匿名对象声明,像这样?
public class CodeArg
{
public string code { get; set; }
public override bool Equals(object obj)
{
if(obj == null || GetType() != obj.GetType())
{
return false;
}
return code == ((CodeArg)obj).code;
}
public override int GetHashCode()
{
return code.GetHashCode();
}
}
string url = _urlProvider.Action("ValidateCode",
new CodeArg { code = "spam-and-eggs" });
IUrlProvider urlProvider = MockRepository.GenerateStub<IUrlProvider>();
urlProvider.Stub(u => u.Action(
Arg<string>.Is.Equal("ValidateCode"),
Arg<CodeArg>.Is.Equal(new CodeArg { code = "spam-and-eggs" }) ))
.Return("http://www.mysite.com/validate/spam-and-eggs");
Run Code Online (Sandbox Code Playgroud)
编辑
如果我的单元测试与我的控制器在同一个项目中,那么比较匿名对象就行了.因为它们是在单独的程序集中声明的,所以它们不相等,即使它们具有相同的字段名称和值.比较由不同命名空间中的方法创建的匿名对象似乎不是问题.
解
我替换Arg<object>.Is.Equal()为Arg<object>.Matches()使用自定义AbstractConstraint:
IUrlProvider urlProvider = MockRepository.GenerateStub<IUrlProvider>();
urlProvider.Stub(u => u.Action(
Arg<string>.Is.Equal("ValidateCode"),
Arg<object>.Matches(new PropertiesMatchConstraint(new { code = "spam-and-eggs" })) ))
.Return("http://www.mysite.com/validate/spam-and-eggs");
public class PropertiesMatchConstraint : AbstractConstraint
{
private readonly object _equal;
public PropertiesMatchConstraint(object obj)
{
_equal = obj;
}
public override bool Eval(object obj)
{
if (obj == null)
{
return (_equal == null);
}
var equalType = _equal.GetType();
var objType = obj.GetType();
foreach (var property in equalType.GetProperties())
{
var otherProperty = objType.GetProperty(property.Name);
if (otherProperty == null || property.GetValue(_equal, null) != otherProperty.GetValue(obj, null))
{
return false;
}
}
return true;
}
public override string Message
{
get
{
string str = _equal == null ? "null" : _equal.ToString();
return "equal to " + str;
}
}
}
Run Code Online (Sandbox Code Playgroud)
匿名类型确实以非常正常的方式实现 Equals 和 GetHashCode,为每个子成员调用 GetHashCode 和 Equals。
所以这应该通过:
Assert.AreEqual(new { code = "spam-and-eggs" },
new { code = "spam-and-eggs" });
Run Code Online (Sandbox Code Playgroud)
换句话说,我怀疑你在错误的地方寻找问题。
请注意,您必须以完全正确的顺序指定属性 - 因此new { a = 0, b = 1 }不会等于new { b = 1, a = 0 }; 这两个对象将具有不同的类型。
编辑:匿名类型实例创建表达式也必须位于同一程序集中。毫无疑问,这就是本案的问题所在。
如果Equals允许您指定 an IEqualityComparer<T>,您可能可以构建一个能够比较具有相同属性的两个匿名类型的方法,方法是从另一种类型的实例的属性创建一种类型的实例,然后将其与原始类型进行比较相同类型。当然,如果您使用嵌套匿名类型,您需要递归地执行此操作,这可能会变得丑陋......
| 归档时间: |
|
| 查看次数: |
2097 次 |
| 最近记录: |