最近,我看到了这段代码。由于我尝试一次学习很少的东西,因此这段代码有问题,但是不知道如何解决。我希望能够对该代码进行单元测试
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}'");
}
}
此代码没有什么问题。
我想确保正确调用该类,并且根据我的阅读,我认为这是定位器模式。
我也想对该类进行单元测试,并且需要帮助以使其可以使用Moq进行单元测试。
由于下面的@ErikPhillips解释,我现在知道使用该类的其他类将不可测试。所以,如果我有一个像下面这样的课程:
public class CarConsumer
{
public void ICar GetRedCar()
{
var result = CarFactory.Create("Tesla");
result.Color = Color.Red;
return result;
}
}
,GetRedCar()方法将很难测试,因为它使用CarFactory静态类,并且对于单元测试或外部客户端,GetRedCar()方法API中没有任何内容表明它依赖于此静态类。
我想重构CarFactory类,以便可以像上面CarConsumer类中的示例一样使用它的其他类进行正确的测试。
答案 0 :(得分:4)
我希望能够对该代码进行单元测试
哪些具体问题使您无法对此类进行单元测试?它有两种方法,针对它编写单元测试似乎很简单。
名称是CarFactory,但是对我来说,这看起来不像是工厂模式
我相信工厂模式是
工厂方法模式是一种创建模式,该模式使用工厂方法来处理创建对象的问题,而不必指定将要创建的对象的确切类
我输入了汽车的名称(因此我没有指定类型),它为我创建了类。就是这样。这是一个很好的例子吗?我不认为,但是我认为它做得如何并不会改变它的本质。
这并不意味着它不是服务定位器,但它绝对是工厂方法。 (老实说,它看起来不像服务定位符,因为它只提供一项服务)
像Moq这样的框架中的单元测试
Moq不是一个单元测试框架。 Moq是一个模拟框架。静态类很难模拟。如果可以模拟它,则可以使用需要模拟类的方法进行单元测试。
静态类..它们也隐藏了依赖项。
任何设计不当的东西都可以做任何事情。静态类不是根据定义来隐藏任何内容的。
在这种情况下,我要说的是,此静态类使您无法轻松模拟它,以对依赖于静态类方法的其他方法进行单元测试。
我还想对该类进行单元测试,并且需要帮助以使其能够使用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;
}
}
我们可以起订这类Func,但这实际上只是解决实际问题的拐杖。
我想我真正的问题是如何制作我的CarFactory,以便可以使用Moq测试其他使用它的类的方法?
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}'");
}
}