C# 测试-尝试仅部分模拟接口的行为(对于现有对象)

C# 测试-尝试仅部分模拟接口的行为(对于现有对象),c#,moq,C#,Moq,我遇到的最初问题是,我正在编写一个集成测试,我想为它伪造一个从IDbConnection实例返回的小值。我不想为每个查询伪造返回值。相反,我希望所有查询实际命中数据库并正常处理,但对于一个SELECT,我希望调整值。我将其设想为一种中间人类型的SELECT查询修改 我正在使用Moq库,也许我正在尝试做的是不可能的。下面是我为一个虚构接口所做尝试的一个通用示例(第三次尝试有效,但需要我手动编写一个包装类,我希望可以避免这种情况): 使用Moq; 使用制度; 名称空间控制台EAPP1 { 公共接口I

我遇到的最初问题是,我正在编写一个集成测试,我想为它伪造一个从IDbConnection实例返回的小值。我不想为每个查询伪造返回值。相反,我希望所有查询实际命中数据库并正常处理,但对于一个SELECT,我希望调整值。我将其设想为一种中间人类型的SELECT查询修改

我正在使用Moq库,也许我正在尝试做的是不可能的。下面是我为一个虚构接口所做尝试的一个通用示例(第三次尝试有效,但需要我手动编写一个包装类,我希望可以避免这种情况):

使用Moq;
使用制度;
名称空间控制台EAPP1
{
公共接口IProcess
{
字符串处理1(T);
字符串处理2(T,TA);
}
公共类处理器:IProcess
{
专用处理器(){}
公共静态处理器CreateProcessorFactoryMethod(){返回新处理器();}
公共字符串Process1(T)=>“Processor.Process1”;
公共字符串Process2(T,TA-TA)=>“Processor.Process2”;
}
公共课程
{
静态void Main(字符串[]参数)
{
IProcess existingProcessorInstance=Processor.CreateProcessorFactoryMethod();
IProcess objectToTest=null;
Mock=null;
int attemptNumber=3;//将此设置为您希望尝试的尝试
开关(尝试编号)
{
案例1://这不起作用。第一个参数实际上是一个构造函数参数,但我希望它是要使用的基础实例。
mock=新的mock(existingProcessorInstance,MockBehavior.Loose);
objectToTest=mock.Object;
打破
案例2://这也不起作用。它认为第一个参数是处理器构造函数的参数,但我希望它是要使用的底层实例。
var mockProcessor=new Mock(existingProcessorInstance){CallBase=true};
objectToTest=mockProcessor.Object;
打破
案例3:
//这是可行的,但我必须为我需要的每个接口编写一个全新的包装器类,这有点痛苦。
var wrapper=new WrapperForIProcess(existingProcessorInstance);
mock=wrapper.mocky;
objectToTest=包装器;
打破
违约:
抛出新的NotImplementedException();
}
//我想模拟这个特定的调用,但是其他的一切都应该通过现有的ProcessorInstance。
mock.Setup(m=>m.Process1(It.Is(s=>s==string.Empty)))
.Returns((字符串t)=>“进程1的模拟”);
Console.WriteLine(objectToTest.Process1(string.Empty));//预期:“模拟Process1”
Console.WriteLine(objectToTest.Process1(“asdf”);//预期为:“Processor.Process1”
Console.WriteLine(objectToTest.Process2(42,1.0));//预期为:“Processor.Process2”
}
/// 
///为尝试而创建#3
/// 
公共类包装器ForIprocess:IProcess
{
专用只读IProcess\u处理器;
IProcess的公共包装器(IProcess处理器)
{
_处理器=处理器;
mockry=新Mock(MockBehavior.Strict);
}
公开模拟嘲弄{get;}
公共字符串Process1(T T)=>WrapFunctionCall(()=>mockry.Object.Process1(T),()=>u processor.Process1(T));
公共字符串Process2(T,TA TA)=>WrapFunctionCall(()=>mockry.Object.Process2(T,TA),()=>u processor.Process2(T,TA));
私有静态T WrapFunctionCall(Func mockFunction、Func wrappedObjectFunction)
{
尝试
{
返回mockFunction.Invoke();
}
捕获(模拟异常)
{
//假设由于模拟中未提供任何实现(以及严格行为)而引发此问题
//所以回到我们正在包装的对象的行为上来。
返回wrappedObjectFunction.Invoke();
}
}
}
}
}

我是否应该采取更好的方法?这在Moq中是可能的吗?

您可以使用Moq模拟
虚拟
抽象
属性/方法。您不模拟的任何成员都将默认为其具体实现(如果是抽象的,则为存根)。因此,如果您关心的成员是虚拟的,那么只需向这些成员添加一个
Setup
,如果需要,您可以(可选)对断言执行
Verify
。@KennethK。因此,我假设您说要像“Mock”一样创建Mock,但这不允许我传入现有实例。如果一个实例只能由工厂方法创建,那么我就不能围绕该实例设置模拟。指的是这样的情况,它不适合我的情况,我有一个由工厂方法创建的接口实例,我想包装这个特定实例,而不是通过一些模拟构造函数创建一个新实例。
using Moq;
using System;
namespace ConsoleApp1
{
    public interface IProcess
    {
        string Process1<T>(T t);
        string Process2<T, TA>(T t, TA ta);
    }

    public class Processor : IProcess
    {
        private Processor() { }
        public static Processor CreateProcessorFactoryMethod() { return new Processor(); }
        public string Process1<T>(T t) => "Processor.Process1";
        public string Process2<T, TA>(T t, TA ta) => "Processor.Process2";
    }

    public class Program
    {
        static void Main(string[] args)
        {
            IProcess existingProcessorInstance = Processor.CreateProcessorFactoryMethod();
            IProcess objectToTest = null;
            Mock<IProcess> mock = null;

            int attemptNumber = 3; //set this to the attempt you wish to try

            switch (attemptNumber)
            {
                case 1: //This doesn't work. First argument is actually a constructor parameter, but I want it to be the underlying instance to use.
                    mock = new Mock<IProcess>(existingProcessorInstance, MockBehavior.Loose); 
                    objectToTest = mock.Object;
                    break;
                case 2: //This also doesn't work. It thinks the first parameter is a parameter for the Processor constructor, but I want it to be the underlying instance to use.
                    var mockProcessor = new Mock<Processor>(existingProcessorInstance) { CallBase = true};
                    objectToTest = mockProcessor.Object;
                    break;
                case 3:
                    //this works, but I have to write a whole new Wrapper class for each interface I need to do this for, which is kind of a pain.
                    var wrapper = new WrapperForIProcess(existingProcessorInstance);
                    mock = wrapper.Mockery;
                    objectToTest = wrapper;
                    break;
                default:
                    throw new NotImplementedException();
            }

            //I want to mock this one specific call, but everything else should go through to the existingProcessorInstance.
            mock.Setup(m => m.Process1<string>(It.Is<string>(s => s == string.Empty)))
                .Returns((string t) => "Mock of Process1");

            Console.WriteLine(objectToTest.Process1<string>(string.Empty));    //expect: "Mock of Process1"
            Console.WriteLine(objectToTest.Process1<string>("asdf"));          //expect: "Processor.Process1"
            Console.WriteLine(objectToTest.Process2<int, double>(42, 1.0));    //expect: "Processor.Process2"   
        }


        /// <summary>
        /// Created for attempt #3
        /// </summary>
        public class WrapperForIProcess : IProcess
        {
            private readonly IProcess _processor;
            public WrapperForIProcess(IProcess processor)
            {
                _processor = processor;
                Mockery = new Mock<IProcess>(MockBehavior.Strict);
            }
            public Mock<IProcess> Mockery { get; }
            public string Process1<T>(T t) => WrapFunctionCall<string>(() => Mockery.Object.Process1(t), () => _processor.Process1(t));
            public string Process2<T, TA>(T t, TA ta) => WrapFunctionCall<string>(()=>Mockery.Object.Process2(t, ta), ()=>_processor.Process2(t,ta));

            private static T WrapFunctionCall<T>(Func<T> mockFunction, Func<T> wrappedObjectFunction)
            {
                try
                {
                    return mockFunction.Invoke();
                }
                catch(MockException)
                {
                    //assuming this is thrown due to no implementation provided in the mock (and strict behavior)
                    //so fall back to the behavior of the object we are wrapping.
                    return wrappedObjectFunction.Invoke();
                }
            }
        }
    }
}