「非仮想(VBでオーバーライド可能)メンバーの設定が無効です...」というメッセージの例外が表示されるのはなぜですか?


176

ブール型を返す非仮想メソッドをモックする必要がある単体テストがあります

public class XmlCupboardAccess
{
    public bool IsDataEntityInXmlCupboard(string dataId,
                                          out string nameInCupboard,
                                          out string refTypeInCupboard,
                                          string nameTemplate = null)
    {
        return IsDataEntityInXmlCupboard(_theDb, dataId, out nameInCupboard, out refTypeInCupboard, nameTemplate);
    }
}

だから私はXmlCupboardAccessクラスのモックオブジェクトがあり、以下に示すように私のテストケースでこのメソッドのモックをセットアップしようとしています

[TestMethod]
Public void Test()
{
    private string temp1;
    private string temp2;
    private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();
    _xmlCupboardAccess.Setup(x => x.IsDataEntityInXmlCupboard(It.IsAny<string>(), out temp1, out temp2, It.IsAny<string>())).Returns(false); 
    //exception is thrown by this line of code
}

しかし、この行は例外をスローします

Invalid setup on a non-virtual (overridable in VB) member: 
x => x.IsDataEntityInXmlCupboard(It.IsAny<String>(), .temp1, .temp2, 
It.IsAny<String>())

この例外を回避する方法の提案はありますか?


テストの何に依存していXmlCupboardAccessますか?
プレストンギロット2014

9
そのシンプルな..あなたはそれをマークする必要がありますvirtual。Moqは、オーバーライドできない具象型をモックできません。
Simon Whitehead

回答:


265

Moqは非仮想メソッドとシールされたクラスをモックできません。モックオブジェクトを使用してテストを実行している間、MOQは実際には「XmlCupboardAccess」から継承するメモリ内プロキシタイプを作成し、「SetUp」メソッドで設定した動作をオーバーライドします。また、C#でご存じのように、Javaの場合とは異なり、仮想としてマークされている場合にのみ、何かをオーバーライドできます。Javaでは、デフォルトですべての非静的メソッドが仮想であると想定しています。

あなたが考慮すべきもう1つのことは、「CupboardAccess」のインターフェースを導入し、代わりにインターフェースのモックを開始することです。それはあなたがあなたのコードを切り離すのを助け、長期的には利益を得るでしょう。

:最後に、のようなフレームワークがあるTypeMockJustMock ILを直接操作するので、非仮想メソッドを模擬することができます。ただし、どちらも市販品です。


59
インターフェースをモックするだけでよいという事実に+1。この質問によって、実行中の問題が解決されました。これは、基になるインターフェイスではなくクラスを誤ってモックしたためです。
Paul Raff、2015

1
これにより問題が解決するだけでなく、テストが必要なすべてのクラスにインターフェースを使用することをお勧めします。Moqは基本的に、他のモックフレームワークの一部がこの原則を回避できるようにするために、適切な依存関係の逆転を強制しています。
Xipooo 2016年

IPeopleRepositoryなどのインターフェイスにFakePeopleRepositoryなどの偽の実装があり、偽の実装をモックしている場合、この原則の違反と見なされますか?私のテストセットアップでは、コンストラクターでインターフェイスを取るサービスクラスに偽のオブジェクトを渡す必要があるため、IoCはまだ保持されていると思います。
paz

1
@paz MOQを使用する重要な点は、偽の実装を回避することです。次に、境界条件などを確認するために必要な偽の実装のバリアントの数を検討します。理論的には、はい、偽の実装を模擬できます。しかし、実際にはコードのにおいのように聞こえます。
Amol 2017

このエラーは実際にはインターフェイスの拡張メソッドで発生する可能性があり、混乱を招く可能性があることに注意してください。
ダンパントリー

34

私と同じ問題を抱えている人のために、インターフェースではなく実装タイプを誤って入力しました。

var mockFileBrowser = new Mock<FileBrowser>();

の代わりに

var mockFileBrowser = new Mock<IFileBrowser>();


5

具象クラスをモックする代わりに、そのクラスインターフェイスをモックする必要があります。XmlCupboardAccessクラスからインターフェイスを抽出する

public interface IXmlCupboardAccess
{
    bool IsDataEntityInXmlCupboard(string dataId, out string nameInCupboard, out string refTypeInCupboard, string nameTemplate = null);
}

そして代わりに

private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();

への変更

private Mock<IXmlCupboardAccess> _xmlCupboardAccess = new Mock<IXmlCupboardAccess>();

3

このエラーは、インターフェイスの拡張メソッドが呼び出されていることを確認している場合にも発生します。

たとえば、あなたがからかっている場合:

var mockValidator = new Mock<IValidator<Foo>>();
mockValidator
  .Verify(validator => validator.ValidateAndThrow(foo, null));

.ValidateAndThrow()IValidator<T>インターフェースの拡張であるため、同じ例外が発生します。

public static void ValidateAndThrow<T>(this IValidator<T> validator, T instance, string ruleSet = null)...


-12

コード:

private static void RegisterServices(IKernel kernel)
{
    Mock<IProductRepository> mock=new Mock<IProductRepository>();
    mock.Setup(x => x.Products).Returns(new List<Product>
    {
        new Product {Name = "Football", Price = 23},
        new Product {Name = "Surf board", Price = 179},
        new Product {Name = "Running shose", Price = 95}
    });

    kernel.Bind<IProductRepository>().ToConstant(mock.Object);
}        

例外を参照してください。


4
あなたの解決策の説明を提供できますか?また、「例外を参照してください...」という問題が残っています。これを拡張できますか?
アマダン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.