首页 > 解决方案 > 通过 Moqs It.IsAny作为方法参数

问题描述

首先是关于我的开发环境的一些信息:

我尝试模拟一个带有一些字符串参数的函数,我想用它It.IsAny<string>()来设置。通常我会这样:

using ( AutoMock mock = AutoMock.GetLoose() ) {
    mock.Mock<SomeInterface>()
                .Setup( x => x.someFunction(
                    It.IsAny<string>(),
                    It.IsAny<string>(),
                    It.IsAny<string>() ) );
//...
}

但是现在我想调用一个进行设置的函数,所以我不必复制粘贴上面的代码并使我的单元测试有点“更好看”。我想象这样的事情:

using ( AutoMock mock = AutoMock.GetLoose() ) {
    UnknownType anyString = It.IsAny<string>();
    setup( mock, anyString );
//...
}

void setup( Automock mock, UnknownType anyString ){
    mock.Mock<SomeInterface>()
            .Setup( x => x.someFunction( anyString, anyString, anyString ) );     
}

有人知道解决方案吗?当我使用string或什var至作为未知类型时,anyString变量nullUnknownType anyString = It.IsAny<string>();. 提前感谢您的回答。

进一步说明:

我需要为每个参数指定不同的值。所以它可能看起来像这样:

using ( AutoMock mock = AutoMock.GetLoose() ) {
   UnknownType string1 = It.IsAny<string>;
   UnknownType string2 = It.Is<string>( s => s.Equals( "Specific string" )  );
   UnknownType string3 = It.IsAny<string>;
   setup( mock, string1, string2, string3 );
//...           
}

private static void setup( AutoMock mock, 
   UnknownType string1, UnknownType string2, UnknownType string3 ) {
   mock.Mock<SomeInterface>().Setup( x => x.someFunction( string1, string2, string3 ) );
}

标签: c#moq

解决方案


It.* is meant to be used as Setup expression arguments and not passed around as parameters/variables as, by default, they return null.

To the best of my knowledge, what you are requesting does not appear to be possible.

The closest thing I can think of through the use of generics is to create an extension method to act on the auto mock that takes the desired expression to mock

public static class AutomockExtension {
    public static Moq.Language.Flow.ISetup<T> Setup<T>(this Automock mock, Expression<Action<T>> expression) where T : class {
        return mock.Mock<T>().Setup(expression);
    }

    public static Moq.Language.Flow.ISetup<T, TValue> Setup<T, TValue>(this Automock mock, Expression<Func<T, TValue>> expression) where T : class {
        return mock.Mock<T>().Setup(expression);
    }
}

which really does not save you much in terms or repeated code as you will still have to invoke It.* in the expression

using ( AutoMock mock = AutoMock.GetLoose() ) {

    mock.Setup<SomeInterface>(_ => 
        _.someFunction(
            It.IsAny<string>(),
            It.Is<string>( s => s.Equals( "Specific string" ),
            It.IsAny<string>()
        ) 
    );

    //...           
}

Original Answer

Use generics

void setup<T>(Automock mock){
    mock.Mock<SomeInterface>()
            .Setup(_ => _.someFunction(
                It.IsAny<T>(), 
                It.IsAny<T>(), 
                It.IsAny<T>()));     
}

which would then let you invoke the setup as needed like

using ( AutoMock mock = AutoMock.GetLoose() ) {
    setup<string>(mock);
    //...
}

推荐阅读