FakeItEasy, Fake a parents virtual method from the child class
我试图在没有成功的情况下(使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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; } |
然后我有从这个基类继承的子类,然后通过重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } } |
我在单元测试中尝试过这种方法,它只配置对子类的方法调用,而不能配置父类。如何配置fake来调用子类和伪造父基类方法?谢谢您。
1 2 3 4 5 6 7 8 9 | 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); |
不,我不认为你能轻易地做你想做的事,用假货。我甚至认为你不应该那样做。
但是,通过将基调用封装到子类中的模板方法中,可以实现类似的功能。只需更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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); } } |
然后像这样更改测试:
1 2 3 4 5 6 7 8 9 10 11 | 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); |
再说一遍,这很难看。我能想象的唯一的情况是,这样做是可以的,将测试添加到您打算(并且将)尽快重构的一些遗留代码中。
希望它有帮助。
有趣的问题。对同一对象中的另一个方法使用Fakeitasy的a.callto()的变体,但它不是"同一对象中的另一个方法",而是"同一对象中的同一方法"(几乎是同一对象中的同一方法)。
我认为这是不可能的。当fakeitiasy伪造它时,它会创建一个子类,该子类有自己的
我认为这种测试验证器的方法有点不寻常,并且会推荐不同的路径。如果您可以选择重新构造生产代码,那么可以组成验证器,而不是在层次结构中定义它们。或者您可以让基础(公共)功能驻留在由validate调用的其他方法中。然后您可以询问该方法,尽管我不推荐后一种方法。
如果您不能(或者不愿意)重构生产代码,另一种方法是测试具体验证器的整体行为。与其查看
根据测试框架的不同,通过参数化基本测试用例,然后为每个具体实现执行它们,可以非常容易地生成所有这些测试。