Using Moq to mock a unsafe interface
Asked Answered
P

3

8

Is it possible to use Moq to mock an unsafe interface? For example I have (MCVE):

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public unsafe void TestMethod1()
    {
        Mock<IMyUnsafeInterface> mockDependency = new Mock<IMyUnsafeInterface>();
        mockDependency.Setup(i => i.DoWork(It.IsAny<int*>())); // error on this line

        systemUnderTest.Dependency = mockDependency.Object;
        ...
    }
}

public unsafe interface IMyUnsafeInterface
{
    void DoWork(int* intPtr);
    byte* MethodNotRelevantToThisTest1(byte* bytePtr);
    ComplexObject MethodNotRelevantToThisTest2();
    ...
}

However I cannot use an unsafe type as a type parameter and I get the error:

Error   1   The type 'int*' may not be used as a type argument  

Is it possible to setup mocks without using a type parameter to avoid this problem?

(I know the obvious solution is to not use an unsafe interface, I am wondering if there is a solution that does work with unsafe interfaces.)

Edit/Update: it is possible to use a stub class, but I would like to avoid that if it is possible to use Moq as Moq provides considerably less verbose code.

public unsafe class MyUnsafeClass : IMyUnsafeInterface
{
    public void DoWork(int* intPtr) {
        // Do something
    }
    public byte* MethodNotRelevantToThisTest1(byte* bytePtr) {
        throw new NotImplementedException();
    }
    public ComplexObject MethodNotRelevantToThisTest2() {
        throw new NotImplementedException();
    }
    ...
}
Panpsychist answered 2/9, 2014 at 10:20 Comment(0)
P
5

Quick answer It's not possible if your type has pointer types in method signatures.


The error you're seeing is because you cannot use a pointer as a type argument. Indeed, in the C# specification you'll find in section 4.4.1 (Type Arguments):

In unsafe code, a type-argument may not be a pointer type.

You can avoid this particular error by changing your code to expect a specific pointer:

Mock<IMyUnsafeInterface> mockDependency = new Mock<IMyUnsafeInterface>();

mockDependency.Setup(i => i.DoWork(any));

// use the mock
mockDependency.Object.DoWork(any);

mockDependency.Verify(p => p.DoWork(any));

However, Moq fails in the Setup call because it's trying to create a Matcher object (used to match setups to invocations) for the argument, which uses the argument's type as a type parameter. This results in the same error as above. Passing your own Match object created via Match.Create or It.Is methods won't work because these methods take a type argument as well.

If you omit the Setup call, taking advantage of the loose mocking behavior, then Moq fails in the Verify call because of the same problem. It's trying to create an object based on the type of the argument parameter so it can match a recorded call with the expression being passed in.

Moq also provides an older method of matching arguments before the It class was provided, where you mark a method with a Matcher attribute:

[Test]
public unsafe void TestMethod1()
{
    int* any = stackalloc int[4];

    Mock<IMyUnsafeInterface> mockDependency = new Mock<IMyUnsafeInterface>();

    // use the mock
    mockDependency.Object.DoWork(any);

    mockDependency.Verify(p => p.DoWork(Is(any)));
}

[Matcher]
public static unsafe int* Is(int* expected) { return null; }
public static unsafe bool Is(int* actual, int* expected)
{
    return actual == expected;
}

This seems like it would work, but it fails with an exception:

System.Security.VerificationException : Operation could destabilize the runtime.
   at lambda_method(Closure)
   at Moq.MatcherFactory.CreateMatcher(Expression expression, Boolean isParams)
   at Moq.MethodCall..ctor(Mock mock, Func`1 condition, Expression originalExpression, MethodInfo method, Expression[] arguments)
   at Moq.Mock.Verify(Mock mock, Expression`1 expression, Times times, String failMessage)
   at Moq.Mock`1.Verify(Expression`1 expression)

I can't quite figure out where this is coming from or how to circumvent it, so that's a dead-end, too.

The best case here would be if you could change int* to IntPtr, which can be mocked normally. If you can't change the type then depending on what you're looking to verify you could make a stub object instead of relying on Moq:

unsafe class StubMyUnsafeInterface : IMyUnsafeInterface
{
    readonly int* expectedIntPtr;

    public StubMyUnsafeInterface(int* expectedIntPtr)
    {
        this.expectedIntPtr = expectedIntPtr;
    }

    public unsafe void DoWork(int* intPtr)
    {
        Assert.That(intPtr == expectedIntPtr);
    }
}

Then in your test:

int* any = stackalloc int[4];
int* other = stackalloc int[4];
var stubMyUnsafeInterface = new StubMyUnsafeInterface(any);

stubMyUnsafeInterface.DoWork(any);   // passes
stubMyUnsafeInterface.DoWork(other); // fails
Pesticide answered 2/9, 2014 at 12:38 Comment(9)
This doesn't seem to answer the question. The question is "how can I use mock to moq this?".Deleterious
@Deleterious Yep, I just expanded, and I'm trying to come up with alternativesPesticide
Yeah, I saw that come up as I hit submit. My main wonder is if there are any less common ways to set up Moq objects without using generics and lambdas but I'm not familiar enough with Moq to say.Deleterious
That sounds like good advice. My original post contained a MCVE, but my actual interface contains several methods; some with int* arguments, some with byte*, and some with methods that I do not want to mock. One of the nice things about Moq is that I only mock the methods that are relevant. Ideally I would like to stick with a methods that uses Mock.Panpsychist
@Panpsychist Understood, though I don't see any way that Moq could possibly support it since calls to Setup/Verify come down to creating a generic Matcher which don't work for pointer types.Pesticide
@Panpsychist Also if you have more than a few methods you don't care about on the interface then it may benefit from from splitting it up, following the interface segregation principle. "No client should be forced to depend on methods it does not use."Pesticide
I agree, but if I could split up the interface then I would just remove the pointers.Panpsychist
@PatrickQuirk - thanks for your help. To make this a great answer for anyone else searching for a similar answer can I suggest that you edit your answer to something like "Short answer: not possible. Longer answer:..."? Or I can edit your answer if you would prefer.Panpsychist
@Panpsychist Good idea, see the edit. Feel free to modify it if you can improve itPesticide
J
0

A code example is provided below on how I typically solve this issue. It involves the following:

  1. Create a new interface (derived from the problematic interface) which declares overloads of each of the methods that takes or returns an unmanaged pointer, and replaces it with IntPtr (since IntPtr is mockable by Moq).

    (Note: if there is a method which differs only by the return type, then I just change the name slightly--e.g. I change Foo() to FooIntPtr

  2. Implement that new interface with a class (that takes in its constructor the new interface created above).

    a. For each of the problematic methods in this class (those with unmanaged pointers), have it it delegate the call to the IntPtr based methods (by doing appropriate casting.
    b. For all other methods, just have them forward to the interface that was passed in via the constructor.

  3. In the test method:

    a. Use Moq to mock the newly created version of the interface. b. Mock the IntPtr version of the methods.
    c. Create an instance of your class from Step 2, and pass in the mock.Object from 3.a. d. Use this instance to pass to the system under test.

In this way, you can continue to use Moq to mock all methods.

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public unsafe void TestMethod1()
    {
        Mock<IMyUnsafeInterfaceForMoq> mockDependency = new Mock<IMyUnsafeInterfaceForMoq>();
        mockDependency.Setup(i => i.DoWork(It.IsAny<IntPtr>())); // use IntPtr instead of int*

        // Use this wrapper whenever passing it to the system under test:
        var dependency = new MyUnsafeInterfaceMoqWrapper(mockDependency.Object);

        var systemUnderTest = new SystemUnderTest();
        systemUnderTest.Dependency = dependency;
    }
}

public unsafe interface IMyUnsafeInterface
{
    void DoWork(int* intPtr);
    byte* MethodNotRelevantToThisTest1(byte* bytePtr);
    bool MethodNotRelevantToThisTest2();
}

// Interface to work around unsafe pointer types by using IntPtr
internal interface IMyUnsafeInterfaceForMoq : IMyUnsafeInterface
{
    void DoWork(IntPtr intPtr);
    IntPtr MethodNotRelevantToThisTest1(IntPtr bytePtr);
}

// Wrapper to workaround unsafe pointer types by using IntPtr
sealed class MyUnsafeInterfaceMoqWrapper : IMyUnsafeInterfaceForMoq
{
    readonly IMyUnsafeInterfaceForMoq _wrapped;
    public MyUnsafeInterfaceMoqWrapper(IMyUnsafeInterfaceForMoq wrapped)
    {
        _wrapped = wrapped ?? throw new ArgumentNullException(nameof(wrapped));
    }

    // For the methods that don't work with Moq, delegate to the methods that *do* work with moq
    // and cast appropriately.
    public unsafe void DoWork(int* intPtr) => DoWork((IntPtr)intPtr);
    public unsafe byte* MethodNotRelevantToThisTest1(byte* bytePtr) => (byte*)MethodNotRelevantToThisTest1((IntPtr)bytePtr);

    // The rest of the methods just forward on.
    public void DoWork(IntPtr intPtr) => _wrapped.DoWork(intPtr);
    public IntPtr MethodNotRelevantToThisTest1(IntPtr bytePtr) => _wrapped.MethodNotRelevantToThisTest1(bytePtr);
    public bool MethodNotRelevantToThisTest2() => _wrapped.MethodNotRelevantToThisTest2();
}

class SystemUnderTest
{
    public IMyUnsafeInterface Dependency { get; set; }
}
Jetport answered 27/2, 2023 at 22:26 Comment(0)
P
-1

To avoid this you can try with Wrapper class concept.You can simply wrap your original class or interface.And after that can use your original function inside Wrapper class function.Like below example -

//Wrapper class
public class MyWrapperClass
{
     public YourReturnType MyFunction()
     {
         OriginalClass obj = new OriginalClass();

         //And inside this function call your original function
         return obj.OriginalFunction();
     }

}

The Wrapper class wrap the original type and we can use/mock the original object according to our need.

If you are not aware the concept of Wrapper class then first understands its concept.

http://www.c-sharpcorner.com/Blogs/12038/wrapper-class-in-C-Sharp.aspx

Publias answered 4/9, 2014 at 7:30 Comment(1)
I'm not quite sure how this applies here. I am aiming to avoid stubbing out the interface by using Moq. I also don't have an OriginalClass - just an interface, so I can't call OriginalFunction() because there is no implementation.Panpsychist

© 2022 - 2024 — McMap. All rights reserved.