【问题标题】:How to setup returned object from static method? UnitTesting如何从静态方法设置返回对象?单元测试
【发布时间】:2021-05-25 01:35:42
【问题描述】:

假设我有一个方法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();
      }
   }
}


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

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

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

【问题讨论】:

  • 如果不修改代码,这是不可能的。您只能模拟虚拟实例方法(或来自接口的方法)

标签: c# .net mocking moq


【解决方案1】:

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

可以添加一个可选参数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();
      }
   }
}

【讨论】:

  • 没有什么能阻止你通过以下方式调用这个方法:ClassWithAMethod.A("email@address.com", () =&gt; (B)null);
  • @PeterCsala 虽然你是对的,但没有什么能阻止 SomeClass.StaticMethod 也返回 null 。这同样适用于您的工厂答案,您的工厂也可以返回 null。基本上你和我的答案是一样的,你的是构造函数注入,我的是方法/函数注入。
  • 是的。我只是想指出,通过允许注入任意用户代码,我们在使用它时必须格外注意。
【解决方案2】:

俗话说:你可以用另一个层次的间接来解决所有问题,除了间接层次太多的问题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 类以依赖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,那么您可以指定一个无参数的 ctor,您依赖于 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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-06
    • 2016-10-18
    • 2019-05-11
    • 1970-01-01
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多