如何设置从静态方法返回的对象?单元测试

How to setup returned object from static method? UnitTesting

假设我有一个方法A

public class ClassWithAMethod
{
   public static void A(string email)
   {
      var b = SomeClass.StaticMethod();
      var c = b.GetUser(x => x.Email == email);
   
      if (c != null)
      {
          throw new Exception();
      }
   }
}


我需要测试一个调用方法 A 的方法 Abc。 例如

public void Abc()
{
   ClassWithAMethod.A("123@gmail.com");
}

所以我需要模拟对象 b 和设置方法 GetUser 但我该怎么做?

如果不修改,我认为这是不可能的。

一个轻微的修改和非破坏性的变化可能是添加一个可选参数 bGetter 来获取 b。 然后,您可以在单元测试中创建自己的 bGetter

public class ClassWithAMethod
{
   public static void A(string email, Func<B> bGetter = null)
   {
      var b = null == bGetter ? SomeClass.StaticMethod() : bGetter();
      var c = b.GetUser(x => x.Email == email);
   
      if (c != null)
      {
          throw new Exception();
      }
   }
}

俗话说:你可以用另一个间接层解决所有问题,除了间接层太多的问题 Reference

通过引入包装器/适配器,您的解决方案变得更加loosely coupled

合同

那么,首先让我们介绍一下下面的界面:

public interface IBFactory
{
  B Create();
}

包装器

让我们创建包装器:

public class BWrapper: IBFactory
{
  public B Create()
  {
    return SomeClass.StaticMethod();
  }
}

备选

在 C# 8 的情况下,您可以依靠 default implementation in interface 功能将前两个步骤合二为一:

public interface IBFactory
{
  public B Create()
  {
    return SomeClass.StaticMethod();
  }
}

依赖抽象

现在让我们修改 ClassWithAMethod class 以依赖 IBFactory:

public class ClassWithAMethod
{
   private readonly IBFactory _bFactory;
   public ClassWithAMethod(IBFactory bFactory)
   {
     this._bFactory = bFactory;
   }
   
   public static void A(string email)
   {
      var b = this._bFactory.Create();
      var c = b.GetUser(x => x.Email == email);
   
      if (c != null)
      {
          throw new Exception();
      }
   }
}

您可以在 DI 容器中将 BWrapper 注册为 IBFactory 的默认实现。或者,如果您既不使用 DI 也不使用 C# 8,那么您可以在依赖 BWrapper 的地方指定一个无参数构造函数。请记住,不建议使用这种方法。这更像是最后的手段。

public class ClassWithAMethod
{
   private readonly IBFactory _bFactory;

   public ClassWithAMethod()
   {
     this._bFactory = new BWrapper();
   }

   public ClassWithAMethod(IBFactory bFactory)
   {
     this._bFactory = bFactory;
   }

   ...
}

单元测试

现在您也可以模拟该依赖关系。

var bFactoryMock = new Mock<IBFactory>();
bFactoryMock.Setup(factory => factory.Create()).Returns(...);

var SUT = new ClassWithAMethod(bFactoryMock.Object);