单元测试工厂/服务定位器 - 静态类

问题描述 投票:0回答:1

最近,我看到这个代码。因为我想立刻去学习一些东西,我有这个代码的问题,但不知道如何解决。我想能够单元测试验证码

public static class CarFactory
{
    private static readonly IDictionary<string, Type> CarsRegistry = new Dictionary<string, Type>();

    public static void Register<TCar>(string car) where TCar : CarBase, new()
    {
        if (!CarsRegistry.ContainsKey(car))
        {
            CarsRegistry.Add(car, typeof(TCar));
        }
    }

    public static ICar Create(string car)
    {
        if (CarsRegistry.ContainsKey(car))
        {
            CarBase newCar = (CarBase)Activator.CreateInstance(CarsRegistry[car]);
            return newCar;
        }

        throw new NotSupportedException($"Unknown '{car}'");
    }
}

我有这个代码一些问题。

  1. 名称是CarFactory但这看起来并不像一个工厂模式给我。它看起来更像是定位模式
  2. 类是静态的 - 我听到静态类是坏像起订量的框架单元测试,而且他们也隐藏依赖。再说另一个常规类中的方法使用了这一点,单元测试,也没有办法知道该方法依赖于这个静态类

我想,以确保该类正确调用,我想根据我的阅读,这是定位模式。

我也想单元测试这个类和需要帮助,使其可以进行单元测试使用起订量。

由于@ErikPhillips解释如下,我现在明白了,使用这个类的其他类将是不可测试。所以,如果我有一个类象下面这样:

public class CarConsumer
{
   public void ICar GetRedCar()
   {
     var result = CarFactory.Create("Tesla");
     result.Color = Color.Red;
     return result;
   }
}

,GetRedCar()方法将是困难的测试,因为它使用CarFactory静态类和用于单元测试或外部客户端,没有什么GetRedCar()方法的API,建议它取决于此静态类。

我想因此使用它的其他类像上述例子CarConsumer类中可以适当地测试,以重构CarFactory类。

c# unit-testing moq factory-pattern service-locator
1个回答
4
投票

我想能够单元测试验证码

有什么具体的问题阻止您的单元测试这个类?它有两个方法,似乎是相当直截了当地对写单元测试。

名称是CarFactory但这看起来并不像一个工厂模式给我

我相信,一个工厂模式

工厂方法模式是使用工厂的方法来处理在创建对象的问题,而不必指定确切的类,将要创建的对象的创建模式

我通过在汽车的名称(所以我还没有指定的类型),它为我创建的类。这几乎是它。它是一个很好的例子?不要在我看来,但我的手它做得怎么样不会改变它是什么意见。

这并不意味着它不服务定位器,但它绝对是一个工厂方法。 (老实说它看起来并不像一个服务定位器,因为它仅提供了一个单一的服务)

在像Moq的框架单元测试

最小起订量不是一个单元测试框架。起订量是一个模拟框架。静态类是不容易的嘲笑。如果你可以模拟它,你可以单元测试与需要嘲笑类方法。

静态类..这也隐藏依赖。

任何设计不当的事情可以做任何事情。静态类是不被定义设计掩饰什么。

我会说在这种情况下,这个静态类,防止您能够轻松地莫克它依赖于静态类的方法单元测试等方法。

我也想单元测试这个类和需要帮助,使其可以进行单元测试使用起订量。

同样,没有受到什么阻碍你从单元测试这个类。

public class CarFactoryTests
{  
  public class MoqCar : CarBase { }

  public void Register_WithValidParameters_DoesNotThrowException
  {
    // Act
    Assert.DoesNotThrow(() => CarFactory.Register<MoqCar>(
      nameof(Register_WithValidParameters_DoesNotThrowException)));
  }

  public void Create_WithValidCar_DoesNotThrowException
  {
    CarFactory.Register<MoqCar>(
      nameof(Create_WithValidParameters_DoesNotThrowException));

    Assert.DoesNotThrow(() => CarFactory.Create(
      nameof(Create_WithValidParameters_DoesNotThrowException));
  }

  // etc etc
}

你可能会碰到的问题

public class CarConsumer
{
   public void ICar GetRedCar()
   {
     var result = CarFactory.Create("Tesla");
     result.Color = Color.Red;
     return result;
   }
}

测试这种方法意味着你是不是在方法的完全控制,因为有GetRedCar()依赖于外部代码。你不能在这里写一个纯粹的单元测试。

这就是为什么你必须CarFactory转换为实例类。然后确保它有什么DI框架,你使用了正确的寿命。

public class CarConsumer
{
   private ICarFactory _carFactory;
   public CarConsumer(ICarFactory carFactory)
   {
     _carFactory = carFactory;
   }

   public void ICar GetRedCar()
   {
     var result = _carFactory.Create("Tesla");
     result.Color = Color.Red;
     return result;
   }
}

现在,我们可以起订量ICarfactory和编写针对GetRedCar()纯粹的单元测试。

不建议如下。

如果出于某种原因你被卡住这种类型的工厂,但你还是要写纯单元测试,你可以这样做:

public class CarConsumer
{
   private Func<string, ICar> _createCar;
   public CarConsumer(Func<string, ICar> createCar= CarFactory.Create)
   {
     _createCar = createCar;
   }

   public void ICar GetRedCar()
   {
     var result = _createCar("Tesla");
     result.Color = Color.Red;
     return result;
   }
}

我们可以MOQ这种类型的函数功能,但它实际上只是为真正的问题拐杖。

我想真正的问题我已经是如何让使用其他类中的方法可以使用起订量进行测试,以我的CarFactory?

public interface ICarFactory
{
  void Register<TCar>(string car) where TCar : CarBase, new();
  ICar Create(string car);
}

public class CarFactory : ICarFactory
{
  private readonly IDictionary<string, Type> CarsRegistry 
    = new Dictionary<string, Type>();

  public void Register<TCar>(string car) where TCar : CarBase, new()
  {
    if (!CarsRegistry.ContainsKey(car))
    {
      CarsRegistry.Add(car, typeof(TCar));
    }
  }

  public ICar Create(string car)
  {
    if (CarsRegistry.ContainsKey(car))
    {
      CarBase newCar = (CarBase)Activator.CreateInstance(CarsRegistry[car]);
      return newCar;
    }

    throw new NotSupportedException($"Unknown '{car}'");
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.