Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Microsoft Fakes support abstract methods on a shim?

I have a class setup in the following manner:

public abstract FooClass {
    public FooClass() {
        // init stuff;
    }

    public void RandomMethod() {
        // do stuff;
    }

    public abstract WhatIWantToShim();
}

What I want to do is set the WhatIWantToShim on the ShimFooClass like so:

ShimFooClass.AllInstances.WhatIWantToShim = () => Boo();

I can set RandomMethod just fine,

ShimFooClass.AllInstances.RandomMethod = () => CalculatePi();

However, it appears that the generated ShimFooClass does not create the WhatIWantToShim property on the AllInstances property of the ShimFooClass.

I've looked at http://msdn.microsoft.com/en-us/library/hh549176.aspx#bkmk_shim_basics but I don't see anything there about abstract methods. The only thing I see referenced that is not supported is finalizers. Anybody know what is going on here and if this scenario is supported?

like image 479
Bart Sipes Avatar asked Apr 04 '13 22:04

Bart Sipes


People also ask

What is Microsoft shims?

In computer programming, a shim is a small library which transparently intercepts an API, changes the parameters passed, handles the operation itself, or redirects the operation elsewhere. Shims can also be used for running programs on different software platforms than they were developed for.

What is shims in c#?

Shim, in C#, is a template class that is derived from a base class with derived classes that inherit the data and behavior of the base class and vary only in the type. The derived class of the shim class reuses the implementation provided by the shim class.


2 Answers

Ahhh....bummer

Interfaces and abstract methods. Stubs provide implementations of interfaces and abstract methods that can be used in testing. Shims can’t instrument interfaces and abstract methods, because they don’t have method bodies.

http://msdn.microsoft.com/en-us/library/hh549175(v=vs.110).aspx

Update: what can be done though is stubbing the shim.

using (ShimsContext.Create())
{
    bool wasAbstractMethodCalled = false;
    var targetStub = new StubFooClass()
    {
        WhatIWantToShim01 = () => wasAbstractMethodCalled = true
    };
    var targetShim = new ShimFooClass(targetStub);
    targetShim.AllInstances.RandomMethod = () => CalculatePi();
    FooClass  target = targetShim.Instance;
    target.WhatIWantToShim();
    Assert.IsTrue(wasAbstractMethodCalled, "The WhatIWantToShim method was not called.");
}

Since the shim cannot handle detouring the WhatIWantToShim method and the stub can, just create a new instance of the stub class and set the detour handler for the abstract method. (Note: the 01 tagged on the end of WhatIWantToShim was added automatically for me when the Fakes were generated in my actual code).

Then just pass the instantiated stub to the constructor of the shim class and shim away as needed.

like image 173
Bart Sipes Avatar answered Sep 17 '22 17:09

Bart Sipes


I'm replying here because I'm quite sure the other answer did not answer the question, and so that future searches return useful information.

Firstly, you cannot shim interfaces. An abstract method is equivalent to an interface. Furthermore, there is no reason to.

{
    bool wasAbstractMethodCalled = false;
    var targetStub = new StubFooClass()
    {
        WhatIWantToShim01 = () => wasAbstractMethodCalled = true
    };
    ShimFooClass.AllInstances.RandomMethod = @class => targetStub.CalculatePi();
    targetStub.WhatIWantToShim();
    Assert.IsTrue(wasAbstractMethodCalled, "The WhatIWantToShim method was not called.");
}

The above is a simplified version of a previous answer, and will simply call the action you just assigned. This is probably not your intent.

Remember WHY you shim. You shim when you want to avoid the effects of a method call within a method you are testing. The abstract method can have no body, and therefore cannot affect anything. The only time this would be useful is in a child class, where the shim would be available to you in the first place.

The only situation in which you might have an issue is if a third class is secretly holding an instance of the abstract class and instantiating it with the child class. You can't fake that. It is, however, terrible design; the instance should be coming from some method (which you can shim) or passed in (Because DI is a good thing!) or else the abstraction is worthless, and you may as well have the instance declared to be the child type, since you aren't using the abstraction in any way.

like image 39
Magus Avatar answered Sep 21 '22 17:09

Magus