使用NSubstitute(或其他)进行统一和自动模拟

时间:2014-11-13 11:45:21

标签: c# unit-testing unity-container nsubstitute automocking

我的问题来自这个问题:Is this possible with Unity (Instead of Castle Windsor)?

以下是答案中的课程:

protected override void Initialize()
{
    var strategy = new AutoMockingBuilderStrategy(Container);

    Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}

class AutoMockingBuilderStrategy : BuilderStrategy
{
    private readonly IUnityContainer container;
    private readonly Dictionary<Type, object> substitutes 
       = new Dictionary<Type, object>();

    public AutoMockingBuilderStrategy(IUnityContainer container)
    {
        this.container = container;
    }

    public override void PreBuildUp(IBuilderContext context)
    {
        var key = context.OriginalBuildKey;

        if (key.Type.IsInterface && !container.IsRegistered(key.Type))
        {
            context.Existing = GetOrCreateSubstitute(key.Type);
            context.BuildComplete = true;
        }
    }

    private object GetOrCreateSubstitute(Type type)
    {
        if (substitutes.ContainsKey(type))
            return substitutes[type];

        var substitute = Substitute.For(new[] {type}, null);

        substitutes.Add(type, substitute);

        return substitute;
    }
}

发布的解决方案适用于SINGLE对象。如果您看到发布的解决方案,则每次调用Resolve时都会返回相同的对象

在以下测试用例中这很好:

var myObjectWithInnerMockedObjects = UnityContainer.Resolve<ISomeInterface>();
var internalAutoMockedObject = UnityContainer.Resolve<IInnerInterface>();

上述问题中公布的解决方案适用于上述情况。

上面的代码通过Unity创建一个对象并尝试解析构造函数参数,如果类型未在unity config中映射,则通过NSubstitute返回一个模拟。

如此有效的链条可能是:

- ActualObject
    - InnerActualObjectDependency
        - MockedDependency (since this was not mapped in Unity, a mock was created)

问题是如果我创建了2个这样的对象,则模拟指向相同的对象。

如果我删除CONTAINS检查,在方法GetOrCreateSubstitute()中,那么每次我都得到一个新的模拟...但是,如何访问特定对象的模拟以设置它的期望? : - (

我希望我对这个问题很清楚!!

1 个答案:

答案 0 :(得分:1)

我们进行了内部团队讨论,并且我更好地理解,模拟意味着是单身人士。 按设计我们不希望为每个创建的对象使用不同的模拟对象。

模拟是针对类而不是方法。因此,以下伪代码将是实现我正在做的事情的完美方式。

var obj1 = Resolve<IMyInterface>();
var obj2 = Resolve<IMyInterface>();
var innerDependency = Resolve<IInnerType>(); // Returns the same object that is shared by above 2 objs

innerDependency.SetExpection(Some expectation);
obj1.PerformAction();
innerDependency.Assert();

innerDependency.SetExpection(Some *different* expectation);
obj2.PerformAction();
innerDependency.Assert();

所以这个问题的借口是错误的。我们没有这样的灵活性,因为我们不想!