模拟 (Moq) 或覆盖密封类中的只读属性? [英] Mock (Moq) or override a readonly propery in a sealed class?

查看:24
本文介绍了模拟 (Moq) 或覆盖密封类中的只读属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一种情况,在 MS Dynamics Crm 中,它使用密封类和只读属性返回一些对象(我只能假设使用内部构造函数或内部属性集).而且这些家伙不是从我可以使用的接口继承的.显然,如果我可以控制这段代码,我会有更多的控制权,但因为它在底层的 MS Dynamics 框架中,所以我有点卡住了.

I have a situation where in MS Dynamics Crm it returns some objects using sealed classes and readonly properties (I can only presume using internal constructors or internal property sets). And these guys don't inherit from an interface I can use. Obviously, if I had control over this code I would have a lot more control, but because it's in the underlying MS Dynamics framework I'm a bit stuck.

我想模拟/覆盖的类称为 别名值.我想嘲笑它的原因是我试图模拟对动态的调用,并想假装我得到了一些别名.

The class I'd like to mock/override is called AliasedValue. The reason I want to mock it is I'm trying to simulate a call to dynamics and want to pretend I'm getting returned some Aliased Values.

所以这里是场景,我想创建一个 Dynamics 将返回的示例实体......像这样:

So here's the scenario, I want to create a sample entity that Dynamics will return... something like this:

var new_entity = new Entity("new_entity")
    {
        Attributes = new AttributeCollection
            {
                {"new_name", "BR"},
                {"createdon", DateTime.Now.AddDays(-1).Date},
                {"new_field", "My field"},
                {"new_contact", new AliasedValue {*****} }
            }
    };

所以我引用了一个联系人"实体(实时返回为 AliasedValue).在测试时,我想确保我的代码处理可能在该字段中返回的某些值(例如,它知道如何处理别名值,并且如果没有返回链接的联系人,则它不会爆炸等).

So I am referencing a "contact" entity (in real time this comes back as an AliasedValue). While testing I want to make sure my code handles certain values that might come back in this field (e.g. it knows how to deal with an aliased value, and that if no linked contact is returned it doesn't blow up, etc etc).

因此,如果您单击 AliasedValue 的链接,您会注意到属性是只读的...所以我无法设置测试数据...

So if you've clicked on the link to AliasedValue you'll notice the properties are read only... so I can't set up test data...

接下来,我将创建自己的类并覆盖整个内容......但它是密封的......

Next up, I was going to create my own class and override the whole thing... but it's sealed...

最重要的是,正如您可能已经猜到的,Moq 不喜欢试图模拟一个密封的类.

And on top of this, as you probably have guessed, Moq doesn't like trying to mock a sealed class.

我读过我需要购买一些更好的"模拟框架才能做到这一点,但我真的不想为了解决这件事而花一些额外的钱.

I've read I'll need to purchase some "better" mocking framework to do this, but I really don't want to have to fork out some extra money just to get around this 1 thing.

有人给我一个很好的小解决方案来解决这个问题吗?

Anyone got a nice neat little solution for me to get around this?

澄清更新

这是如何返回的示例如下.我有一个服务,它将被模拟以返回上述对象.或者它实际返回的是一个包含上述对象的集合.所以是这样的:

A sample of how this is returned is as follows. I have a service which will be mocked to return the above object. Or what it actually returns is a collection containing the above object. So this is like so:

var service = new Mock<IOrganizationService>();
service
    .Setup(s => s.RetrieveMultiple(null))
    .Returns(new EntityCollection (new List<Entity> {new_entity}));

这太好了,我可以返回上述实体并用它做我想做的事.因此,在我创建了上述实体并通过我的模拟服务返回它之后,我在EntityCollection"中得到了上述内容.一旦我有了我的实体,我就可以按如下方式访问该属性:

This is great, I can return the above entity and do what I want with it. So, after I have created the above entity and I return it via my mocked service I get the above in my "EntityCollection". And once I have my entity I access the property as follows:

var aliasedContact = (AliasedValue)new_entity.Attributes["new_contact"];

也许我很傻,但我不太明白答案(由 Lunivore 提供)将如何解决这个问题...(添加评论以获取反馈...)

Maybe I'm being silly but I can't quite get how the answer (by Lunivore) will solve this... (adding a comment to get feedback...)

推荐答案

通过添加一个非常简单的适配器类,您可以从第三方库中抽象出来,您可以通过检查来测试它.与其扩展你的目标类,不如在它的顶部组合你的新类.它可以具有相同的方法名称、相同的属性等(尽管它不是必须的)——它只是委托给密封类.

Abstract yourself from third-party libraries by adding an adapter class that's so simple, you could test it by inspection. Rather than extending your target class, just compose your new class over the top of it. It can have the same names of methods, the same properties, etc. (though it doesn't have to) - it just delegates to the sealed class.

如果您有一个返回密封类的类,请将它们都包装起来.所以它可能看起来像这样:

If you have one class which returns your sealed class, wrap them both. So it might look like this:

var crmWrapper = new MSDynamicsCrmWrapper(_myMsDynamicsCrm);
var entityWrapper = crmWrapper.AliasedValue(url);

并且 crmWrapper 会做:

and the crmWrapper will do:

public EntityWrapper AliasedValue(Url url) 
{
    var entity = delegateCrm.AliasedValue(url);
    return new EntityWrapper(entity);
}

简单.你只需要委托那些你实际使用的方法.

Simple. You only need to delegate those methods you're actually using.

这不仅可以让您模拟该类,而且还可以让您控制,例如,下一个版本的 MS Dynamics 是否稍微更改了 API,或者结果是奇怪的行为或性能不佳.此外,您正在将 API 缩小到您实际使用的部分,因此代码可能更易于维护.

Not only does this allow you to mock the class out, but it gives you control if, say, the next version of MS Dynamics changes the API slightly, or there turns out to be weird behavior or poor performance. Also, you're narrowing down the API to the bits of it that you're actually using, so the code may even be easier to maintain.

计算中没有什么是另一层抽象无法解决的......

There's nothing in computing that another layer of abstraction won't solve...

这篇关于模拟 (Moq) 或覆盖密封类中的只读属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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