如何防止虚拟方法被嘲笑?
我们有一个基类为INotifyPropertyChanged
提供了一些默认实现(这个类被许多其他类使用,不能轻易更改):
public class ObservableBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; // this is virtual so derived classes can override it (rarely used, but it is used) protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } }
现在我有一个接口和一个从ObservableBase
派生的抽象基类,并实现该接口提供一些默认实现(主要用于属性):
public interface INamedTrigger { string Name { get; } void Execute(); } public abstract class ObservableNamedTriggerBase : ObservableBase, INamedTrigger { private string _Name; public string Name { get { return _Name; } set { _Name = value; OnPropertyChanged("Name"); } } public abstract void Execute(); }
现在我想unit testingObservableNamedTriggerBase
的默认实现(使用NUnit和RhinoMocks):
[TestFixture] public class ObservableNamedTriggerBaseTest { [Test] public void TestNameChangeRaisesPropertyChanged() { var prop = ""; var mocks = new MockRepository(); var namedBase = mocks.PartialMock(); namedBase.PropertyChanged += (s, e) => prop = e.PropertyName; namedBase.Name = "Foo"; Assert.AreEqual("Name", prop); } }
不幸的是,这个测试失败了,因为Rhino似乎覆盖了ObservableBase
的虚拟OnPropertyChanged
方法。
这个SO问题和Rhino文档表明PartialMock
应该不会这样做。 另一方面,这个SO问题的答案表明,无论模拟的类型如何,Rhino总是覆盖虚方法。
那么文档错了吗? 我该如何正确测试呢?
更新 :如果我创建自己的模拟类,只为抽象方法提供虚拟实现,那么测试通过,但我想使用Rhino模拟来节省我正是这样做的工作。
不确定您使用的是哪个版本,但似乎您将RR( Record-Replay )语法用法与最近的AAA( Arrange-Act-Assert )混合使用。 基本上,这样做:
var repo = new MockRepository(); var mock = repo.PartialMock();
还需要你至少这样做:
repo.ReplayAll();
缺少这些,会导致repo.Record()
行为不端(如果您设置期望/存根,则可能需要调用repo.Record()
)。 考虑到现代语法可用( 从 Rhino 3.5 开始),这是不必要的努力:
// no repository needed at all var mock = MockRepository.GeneratePartialMock();
MockRepository
上的新静态方法正在处理引擎下的记录重放设置。
一个想法是创建一个测试子类,该方法不可覆盖。
public abstract class TestingObservableNamedTriggerBase : ObservableNamedTriggerBase { protected override sealed void OnPropertyChanged(string propertyName) { base.OnPropertyChanged(propertyName); } }
然后通过这个子类测试它。
我没有测试过这是否有效……
您可以使用Rhinomocks的CallOriginalMethod方法来调用原始方法。
namedBase.Expect(x => x.OnPropertyChanged(Arg.Is.Anything)) .CallOriginalMethod(OriginalCallOptions.NoExpectation);
如果测试中的类包含来自测试程序集的其他程序集,则可能需要使OnPropertyChanged
方法protected internal
并为测试程序集添加InternalVisibleToAttribute
。
如您所知,将InternalVisibleToAttribute("DynamicProxyGenAssembly2")
到模拟内部类型。
您可以使用CallBase
布尔属性来告诉moq是使用原始虚方法还是使用默认值覆盖它们。 正如它在财产摘要中所说:
CallBase摘要:如果没有匹配设置,是否将为模拟类调用基本成员虚拟实现。 默认为false。
所以使用这个:
mock.CallBase = true;