【发布时间】:2011-05-19 14:49:13
【问题描述】:
在我的 MVC3 项目中,我使用 IUrlProvider 接口来包装 UrlHelper 类。在我的一个控制器操作中,我有一个这样的调用:
string url = _urlProvider.Action("ValidateCode", new { code = "spam-and-eggs" });
我想在我的单元测试中存根这个方法调用,它在一个单独的项目中。测试设置如下所示:
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");
很遗憾,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");
编辑
如果我的单元测试与我的控制器在同一个项目中,那么比较匿名对象就可以了。因为它们是在单独的程序集中声明的,所以它们不会相等,即使它们具有相同的字段名称和值。比较不同命名空间中的方法创建的匿名对象似乎不是问题。
解决方案
我使用自定义 AbstractConstraint 将 Arg<object>.Is.Equal() 替换为 Arg<object>.Matches():
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;
}
}
}
【问题讨论】:
-
匿名类型已经覆盖
Equals和GetHashCode。 -
下面的答案适用于盒装值类型。 stackoverflow.com/a/7594196/1011470
标签: c# unit-testing c#-4.0 rhino-mocks