Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Moq to mock a unsafe interface

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();
    }
    ...
}
like image 635
Jonny Avatar asked Sep 02 '14 10:09

Jonny


People also ask

What can be mocked with Moq?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces.

How do you mock a method in Moq?

First, we instantiate the FakeDbArticleMock class and indicate which setup we want to use for this test. Then, it is necessary to instantiate the repository we want to test and inject the mock instance into it. Finally, we call the method we are testing and assert the results.

How do I mock a class without an interface?

Simply mark any method you need to fake as virtual (and not private). Then you will be able to create a fake that can override the method. Save this answer.

What is CallBase in Moq?

CallBase , when initialized during a mock construction, is used to specify whether the base class virtual implementation will be invoked for mocked dependencies if no setup is matched. The default value is false . This is useful when mocking HTML/web controls of the System.


1 Answers

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
like image 150
Patrick Quirk Avatar answered Sep 29 '22 17:09

Patrick Quirk