简体   繁体   English

使用Moq模拟不安全的界面

[英]Using Moq to mock a unsafe interface

Is it possible to use Moq to mock an unsafe interface? 是否可以使用Moq来模拟不安全的界面? For example I have (MCVE): 例如我有(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. 编辑/更新 :可以使用存根类,但我想避免使用Moq,因为Moq提供了相当少的详细代码。

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();
    }
    ...
}

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): 实际上,在C#规范中,您将在第4.4.1节(类型参数)中找到:

In unsafe code, a type-argument may not be a pointer type. 在不安全的代码中,type-argument可能不是指针类型。

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. 但是,Moq在Setup调用中失败,因为它试图为参数创建一个Matcher对象(用于匹配调用的设置),该参数使用参数的类型作为类型参数。 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. 传递通过Match.CreateIt.Is方法创建的自己的Match对象将不起作用,因为这些方法也采用类型参数。

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. 如果省略Setup调用,利用松散的模拟行为,则Moq在Verify调用中失败,因为同样的问题。 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: Moq还提供了一种在提供It类之前匹配参数的旧方法,其中使用Matcher属性标记方法:

[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. 这里最好的情况是如果你可以将int*更改为IntPtr ,可以正常模拟。 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: 如果您无法更改类型,那么根据您要查看的内容,您可以创建存根对象而不是依赖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

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类概念。您可以简单地包装您的原始类或接口。之后可以在Wrapper类函数中使用您的原始函数。如下例所示 -

//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. Wrapper类包装了原始类型,我们可以根据需要使用/ mock原始对象。

If you are not aware the concept of Wrapper class then first understands its concept. 如果您不了解Wrapper类的概念,那么首先要了解它的概念。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM