Arg< object> .Is.Equal与匿名对象 [英] Arg<object>.Is.Equal with anonymous objects

查看:237
本文介绍了Arg< object> .Is.Equal与匿名对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的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& (new {code =spam-and-eggs})))
。返回(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



所以,有一个替代的语法,我可以使用与在不同的程序集中声明的匿名类型。



或者我应该用一个类来替换匿名对象声明,像这样?

  public class CodeArg 
{
public string code {组; }

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})))
。返回(http://www.mysite.com/validate/spam-and-eggs);

EDIT



如果我的单元测试与我的控制器在同一个项目中,比较匿名对象会正常工作。因为它们在不同的程序集中声明,它们将不相等,即使它们具有相同的字段名称和值。比较由不同命名空间中的方法创建的匿名对象似乎不是问题。



解决方案



我替换 Arg .Is.Equal()与 Arg< object> .Matches()使用自定义AbstractConstraint:

  IUrlProvider urlProvider = MockRepository.GenerateStub< IUrlProvider>(); 

urlProvider.Stub(u => u.Action(
Arg< string> .Is.Equal(ValidateCode),
Arg< object& 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(equalType.GetProperties()中的var属性)
{
var otherProperty = objType.GetProperty(property.Name);
if(otherProperty == null || property.GetValue(_equal,null)!= otherProperty.GetValue(obj,null))
{
return false;
}
}
return true;
}

public override string消息
{
get
{
string str = _equal == null? null:_equal.ToString();
return等于+ str;
}
}
}


解决方案>

匿名类型 以非常正常的方式实现Equals和GetHashCode,为每个子成员调用GetHashCode和Equals。



这应该通过:

  Assert.AreEqual(new {code =spam-and-eggs},
new {code =spam-and-eggs});

换句话说,我怀疑你在错误的地方寻找问题。



请注意,您必须按照正确的顺序指定属性 - new {a = 0,b = 1} 不等于 new {b = 1,a = 0} ;这两个对象将是不同类型的。



编辑:匿名类型实例创建表达式也必须在同一个程序集中。这是毫无疑问的问题。



如果 Equals 允许您指定 IEqualityComparer< T> ,你可以创建一个能够通过从另一个实例的属性创建一个类型的实例来比较具有相同属性的两个匿名类型,然后比较对原来的同类型。当然,如果你使用嵌套的匿名类型,你需要递归地执行,这可能会变得丑陋...


In my MVC3 project, I use an IUrlProvider interface to wrap the UrlHelper class. In one of my controller actions, I have a call like this:

string url = _urlProvider.Action("ValidateCode", new { code = "spam-and-eggs" });

I want to stub this method call in my unit test, which is in a separate project. The test setup looks something like this:

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");

Unfortunately, Arg<object>.Is.Equal(new { code = "spam-and-eggs" }) doesn't work, because new { code = "spam-and-eggs" } != new { code = "spam-and-eggs" } when the anonymous types are declared in different assemblies.

So, is there an alternative syntax I can use with Rhino Mocks to check for matching field values between anonymous objects across assemblies?

Or should I replace the anonymous object declarations with a class, like this?

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");

EDIT

If my unit test was in the same project as my controller, comparing the anonymous objects would work fine. Because they are declared in separate assemblies, they will not be equal, even if they have the same field names and values. Comparing anonymous objects created by methods in different namespaces doesn't seem to be a problem.

SOLUTION

I replaced Arg<object>.Is.Equal() with Arg<object>.Matches() using a custom 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;
        }
    }
}

解决方案

Anonymous types do implement Equals and GetHashCode in a pretty normal way, calling GetHashCode and Equals for each of their submembers.

So this should pass:

Assert.AreEqual(new { code = "spam-and-eggs" },
                new { code = "spam-and-eggs" });

In other words, I suspect you're looking for the problem in the wrong place.

Note that you have to specify the properties in exactly the right order - so new { a = 0, b = 1 } will not be equal to new { b = 1, a = 0 }; the two objects will be of different types.

EDIT: The anonymous type instance creation expressions have to be in the same assembly, too. This is no doubt the problem in this case.

If Equals allows you to specify an IEqualityComparer<T>, you could probably build one which is able to compare two anonymous types with the same properties by creating an instance of one type from the properties of an instance of the other, and then comparing that to the original of the same type. Of course if you were using nested anonymous types you'd need to do that recursively, which could get ugly...

这篇关于Arg&lt; object&gt; .Is.Equal与匿名对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆