4

我们有一个提供一些默认实现的基类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();
}

现在我想对ObservableNamedTriggerBase(使用 NUnit 和 RhinoMocks)的默认实现进行单元测试:

[TestFixture]
public class ObservableNamedTriggerBaseTest
{
    [Test]
    public void TestNameChangeRaisesPropertyChanged()
    {
        var prop = "";
        var mocks = new MockRepository();
        var namedBase = mocks.PartialMock<ObservableNamedTriggerBase>();
        namedBase.PropertyChanged += (s, e) => prop = e.PropertyName;
        namedBase.Name = "Foo";
        Assert.AreEqual("Name", prop);
    }
}

不幸的是,这个测试失败了,因为 Rhino 似乎覆盖OnPropertyChangedObservableBase.

这个SO questionRhino 文档表明不应该PartialMock这样做。另一方面,这个SO question的答案表明,无论模拟类型如何,Rhino 总是覆盖虚拟方法。

那么文档有错吗?我该如何正确测试呢?

更新:如果我创建自己的模拟类,仅为抽象方法提供虚拟实现,则测试通过,但我想使用 Rhino 模拟来节省我做这件事的工作。

4

4 回答 4

2

不确定您使用的是哪个版本,但您似乎将 RR(Record-Replay)语法用法与更新的 AAA(Arrange-Act-Assert)混合在一起。基本上,这样做:

var repo = new MockRepository();
var mock = repo.PartialMock<ObservableNamedTriggerBase>();

还要求您至少这样做:

repo.ReplayAll();

缺少它,将导致模拟行为不端(repo.Record()如果您也设置期望/存根,则可能需要调用)。考虑到现代语法可用( Rhino 3.5开始),这是不必要的努力:

// no repository needed at all
var mock = MockRepository.GeneratePartialMock<ObservableNamedTriggerBase>();

新的静态方法MockRepository正在处理引擎盖下的记录重放设置。

于 2013-01-10T09:45:51.630 回答
1

一个想法是创建一个方法不可覆盖的测试子类。

public abstract class TestingObservableNamedTriggerBase : ObservableNamedTriggerBase {
  protected override sealed void OnPropertyChanged(string propertyName) {
    base.OnPropertyChanged(propertyName);
  }
}

然后通过这个子类进行测试。

我还没有测试这是否有效......

于 2013-01-10T04:00:05.507 回答
1

您可以使用CallBase布尔属性来告诉 moq 是使用原始虚拟方法还是使用默认值覆盖它们。正如它在其财产摘要中所说:

CallBase 摘要:如果没有匹配的设置,是否将为模拟类调用基成员虚拟实现。默认为假。

所以使用这个:

mock.CallBase = true;

于 2017-04-10T09:11:36.863 回答
0

您可以使用 Rhinomocks 的CallOriginalMethod方法来调用原始方法。

namedBase.Expect(x => x.OnPropertyChanged(Arg<string>.Is.Anything))
         .CallOriginalMethod(OriginalCallOptions.NoExpectation);

如果测试下的类包含来自测试程序集的其他程序集,您可能需要为测试程序集制作OnPropertyChanged方法protected internal并添加InternalVisibleToAttribute

同样如您所知,添加InternalVisibleToAttribute("DynamicProxyGenAssembly2")到模拟内部类型。

于 2013-01-10T05:16:43.707 回答