[英]FakeItEasy, Fake a parents virtual method from the child class
我試圖從孩子那里偽造對父母公共虛擬驗證方法的調用而沒有成功(使用FakeItEasy
。我有一個基類來驗證類似命令類的簡單命令(為簡單起見,我減少了代碼):
public class CanSetSettings<TSettings> : IValidationhandler<TSettings> where TSettings : ISetting
{
protected readonly IFooRepository Repository;
protected List<ValidationResult> Results;
public CanSetSettings(IFooRepository repository)
{
if (Repository== null)
throw new ArgumentNullException("IFooRepository ", "IFooRepository is needed to validate the command.");
repository = repository;
Results = new List<ValidationResult>();
}
public virtual ICollection<ValidationResult> Validate(TSettings settings)
{
if (settings == null)
{
Results.Add(new ValidationResult("Settings", "The command to validate cannot be missing."));
return Results;
}
if (Repository.Query(settings.Location) == null)
Results.Add(new ValidationResult("Location", "No Location was found for your settings."));
return Results;
}
然后我有從這個基類繼承的子類,然后通過重寫Validate
(簡化我的代碼)來實現它們的特定邏輯。
public class CanSetSpecificSetting : CanSetSettings<SetSpecificSettings>, IValidationhandler<SetSpecificSettings>
{
public CanSetSpecificSetting (IFooRepository repo)
: base(repo)
{ }
public override ICollection<ValidationResult> Validate(SetSpecificSettings command)
{
base.Validate(command); // TODO determine if this call was made
// ... other logic here
return results;
}
}
我在單元測試中嘗試了這個,它只配置對子類的方法調用,我無法配置父類。 如何配置假調用子類並偽造父基類方法? 謝謝。
var _repository = A.Fake<IFooRepository>();
var _validator = A.Fake<CanSetSpecificSetting>(opt => opt.WithArgumentsForConstructor(new object[] { _repository }));
A.CallTo(() => _validator.Validate(_cmd)).CallsBaseMethod().Once();
_validator.Validate(_cmd);
// this passes, but only because it knows about the child call
A.CallTo(() => _validator.Validate(_cmd)).MustHaveHappened(Repeated.Exactly.Once);
不,我認為你不能輕易使用FakeItEasy做你打算做的事情。 我甚至認為你不應該這樣做。
但是你可以通過將基本調用封裝到子類中的模板方法中來實現類似的功能。 只需更改CanSetSpecificSetting
,如下所示:
public class CanSetSpecificSetting : CanSetSettings<SetSpecificSettings>, IValidationhandler<SetSpecificSettings>
{
public CanSetSpecificSetting (IFooRepository repo)
: base(repo)
{ }
public override ICollection<ValidationResult> Validate(SetSpecificSettings command)
{
BaseValidate(command); // << Instead of calling base.Validate(command).
// ... other logic here
return results;
}
// This is the template method. You MUST declare it as virtual.
protected virtual ICollection<ValidationResult> BaseValidate(SetSpecificSettings command)
{
return base.Validate(command);
}
}
然后改變你的測試:
var _repository = A.Fake<IFooRepository>();
var _validator = A.Fake<CanSetSpecificSetting>(opt => opt.WithArgumentsForConstructor(new object[] { _repository }));
A.CallTo(() => _validator.Validate(_cmd)).CallsBaseMethod().Once();
// *** ADD THIS LINE *** must configure it this way because the template method is protected - we don't want to make it public!
A.CallTo(_validator).WhereMethod(x => x.Name == "BaseValidate").Returns("whatever-you-want-to-return");
_validator.Validate(_cmd);
A.CallTo(() => _validator.Validate(_cmd)).MustHaveHappened(Repeated.Exactly.Once);
再次,這是丑陋的 。 唯一可以想象的情況是,這樣做可以將測試添加到您打算(並且將會)盡快重構的一些遺留代碼中。
希望能幫助到你。
有趣的問題。 使用FakeItEasy的A.CallTo()對同一對象中的另一個方法的變體 ,除了它不是“同一對象中的另一個方法”,因為它是“(幾乎)相同對象中的相同方法”。
我不認為這是可能的。 當FakeItEasy偽造時,它會創建一個具有自己的Validate
定義的子類。 我們可以查詢該方法,但是沒有辦法修改任何基類的行為,因此我們不能詢問類是否調用了它的基類。
我認為這種測試驗證器的方法有點不尋常,並建議采用不同的方法。 如果您可以選擇重構生產代碼,則可以編寫驗證器而不是在層次結構中定義驗證器。 或者您可以將基本(公共)功能駐留在由Validate調用的其他方法中。 然后你可以詢問那個方法,雖然我不建議采用后一種方法。
如果您不能(或不願意)重構生產代碼,則可以選擇測試具體驗證器的整體行為。 不要查看CanSetSpecificSetting
調用它的基礎,而是考慮檢查它是否在不同條件下正常運行,例如當存儲庫為null時。 這不僅更容易(可能)驗證,該方法將提供更好的結果 - 確保您的整個系統按照您希望的方式運行,而不僅僅是某些方法確定調用其父級。
根據您的測試框架,通過參數化基本測試用例然后為每個具體實現執行它們,可以非常容易地生成所有這些測試。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.