[英]Unit Testing Factory/Service Locator - Static class
最近,我看到了這段代碼。 由於我嘗試一次學習很少的東西,因此這段代碼有問題,但是不知道如何解決。 我希望能夠對該代碼進行單元測試
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類中的示例那樣使用它的其他類可以正確測試。
我希望能夠對該代碼進行單元測試
哪些特定的問題阻止您對此類進行單元測試? 它有兩種方法,編寫單元測試似乎很簡單。
名稱是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;
}
}
現在我們可以Moq ICarfactory
並針對GetRedCar()
編寫純單元測試。
不建議以下內容。
如果出於某種原因而對這種類型的Factory感到困惑,但仍想編寫純單元測試,則可以執行以下操作:
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這種類型的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}'");
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.