我如何使用 Moq 来模拟一个接口,该接口具有将具体 类 作为参数的方法?

How do I use Moq to mock an interface that has methods that take concrete classes as parameters?

  1. 我有这种情况:一个接口有 2 个方法
  2. 这 2 种方法接受请求和 return 响应
  3. 方法包含内部功能(检查权限并验证请求并使用 entity framework 从数据库获取数据)。
  4. 但我想测试方法而不仅仅是界面。
  5. 我已经成功测试了界面,但现在我想进入方法并在其中进行测试。

代码示例:

 public interface IMyInterface
    {
      [OperationContract]
      responseObject GetData(Service<RequestObject> request);
    }

    public class MyConcreteClass : IMyInterface
    {
       public responseObject GetData(Service<RequestObject> request)
       {
          CheckForNull(request);
          ValidateMethod(request);
          //connect to db
          using(var context = new contextEntity)
          {
            //get data
          }
       }
    }

现在,我想测试检查空值、权限和数据访问,可以吗?还是我必须从内部方法中提取接口?

PS,这是为了我的单元测试。我正在尝试消除外部依赖性。 请详细说明

不应直接需要单元测试私有方法,只能通过 public 方法间接使用。如果您认为您测试的 public 方法不够精确,可能是因为该方法和 class 已经太复杂了。

在这种情况下,请考虑在新代码所在的位置创建一个或多个新的 classes。这样你就可以通过 public 方法对你的代码进行单元测试。额外的好处是您的代码在 Single responsibility principle.

方面可能更好

嘲笑的原因是你可以控制行为;在这种情况下,我会冒险猜测 Service<RequestObject> 实际上没有任何行为,因此它实际上 不需要 嘲笑。所以它可能可以在任何测试中按原样通过。

但是,如果它 确实 有行为,并且该行为跨越架构边界(假设在 GetData 方法中,您正在 request 将进行网络调用或访问文件系统等的参数)然后你需要模拟它。但是你可以很容易地做到这一点:

public interface IService<RequestObject>
{
   //put method and property signatures of Service<RequestObject> here
}

public class ServiceObject:Service<RequestObject>, IService<RequestObject>
{
   public ServiceObject(RequestObject request): base(request){
        //or however the Service<Request> object is instantiated.
   };
}

然后把GetData改成IService<RequestObject>;现在您的调用代码可以实例化 ServiceObject 来代替 Service 并将其传递给 GetData 方法。另外,您可以根据需要在界面上模拟任何方法。显然,如果您不控制调用代码,那将是一个问题,因为编写该代码的人需要这样做,但这是您需要与他们进行的对话:)

在测试内部操作方面,您需要了解如何抽象 GetData 方法使用的任何依赖行为 - 例如,contextEntityCheckForNull, ValidateMethod, 等等——所有这些都是被提取到它们自己的抽象中的候选对象,并且 injected 作为依赖项被提取到 MyConcreteClass 中,例如:

public class MyConcreteClass: IMyInterface
{
   readonly INullChecker _nullChecker;
   readonly IValidator _validator;
   readonly IContextEntity _context;

   public MyConcreteClass(INullChecker nullChecker, IValidator validator, IContextEntity _context)
   {
      _nullChecker = nullChecker;
      _validator = validator;
      _context=context;
   }

   public responseObject GetData(Service<RequestObject> request)
   {
       _nullChecker.Check(request)//**;
       _validator.Validate(request);
       var result = _context.DoSomethingWith(request);
       return result;
   }
}

现在您可以为 MyConcreteClass 编写测试并使用依赖项的模拟实现,以确保 GetData 方法正确使用它们。

**我猜这可以用一个简单的 if request==null throw new ArgumentNullException() 代替,它更干净、更简单。