0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【C#】ユニットテストでMockを作るとUnsupported expression:~Non-overridable membersとなった際の対策法。

Last updated at Posted at 2021-07-29

はじめに

ASP.NET Core C# + NUnitで開発をしていた際にユニットテストで詰まった部分について備忘録として残しておきます。
ユニットテストでアプリ側のクラスを生成し、コンストラクタ引数をモックで作成してテストをした際に「System.NotSupportedException : Unsupported expression:~Non-overridable members (here: ~~) may not be used in setup」というエラーが出てしまいました。
unittest.png

今回はその解決方法を2つほど記載していこうと思います。

サンプルコード

アプリ側が以下のコードになっている場合を考えてみましょう。
利用しているのはASP.NET Core コンソールアプリです(簡略化のためProgram.csのMain関数は省略しています)。
appSettings.jsonファイルから「Test1」キーの値を引っ張ってきてその値が9999だったら0を返却し、そうでない場合は1を返却するというプログラムです。

program.cs
public class TestClass : ConsoleAppBase
{
    private readonly TestConfig _testConfig;

    public TestClass(IOptions<TestConfig> options)
    {
        _testConfig = options.Value;
    }

    [Command("test")]
    public void Test1()
    {
        Console.WriteLine(WriteNumber());
    }

    public int WriteNumber()
    {
        if(_testConfig.Test1 == 9999)
        {
            return 0;
        }
        else
        {
            return 1;
        }
    }
} 
TestConfig.cs
namespace ConsoleApp2
{
    public class TestConfig
    {
        public int Test1 { get; set; }
    }
}

テストコードは以下のように作成してみました。

UnitTest1.cs
[Test]
public void WriteNumberMethodTest()
{
    var testConfigMock = new Mock<IOptions<TestConfig>>();
    testConfigMock.Setup(o => o.Value.Test1).Returns(9999);
    var target = new TestClass(testConfigMock.Object);
    // appSettings.jsonのTest1キーが9999時は0が返却される
    Assert.AreEqual(0, target.WriteNumber());

    testConfigMock.Setup(o => o.Value.Test1).Returns(9998);
    var target2 = new TestClass(testConfigMock.Object);
    // appSettings.jsonのTest1キーが9999以外の時は1が返却される
    Assert.AreEqual(1, target.WriteNumber());
}

コンソールアプリ自体は正常に動作しますがユニットテストでは最初に記載した通りエラーが出てしまいます。

エラーの原因

色々と調べていると世界には同じ内容で詰まっている方がいるものです。

Moq開発メンバーが回答してくれていますが、どうやらMoqはvirtualを付ける必要があるようです。
(インターフェースを対象とすればオーバーライドの心配はしなくても大丈夫です。)

そこで元のコードを見ると、確かに・・・Test1はvirtualになっていないですね・・・

virtual修飾子を付けてみる

そこでvirtual修飾子を付けてみましょう。

TestConfig.cs
namespace ConsoleApp2
{
    public class TestConfig
    {
        public virtual int Test1 { get; set; }
    }
}

テストを実行~~~
しっかりテストが通ったのが確認できましたね!
unittest2.png

もう1つの解決方法

こちらは公式ではないですが、stackoverflowで以下のような回答がありました。

Options.Createについて調べてみたら単純に自身のインスタンスをOptionsの型で返すようです。

こちらだとMoqを使用していないので先ほどのようにvirtualを付ける必要もないので良さげです。
どちらが良いのかは・・・ちょっと分かりませんが好みという感じなんですかね??

おわりに

記事を書きながらMoqやOptionsについて調べているといかに自分が雰囲気でそれらを使っていたかが分かります。
なかなか実務の時間にじっくり深堀りする余裕がないので仕方ないといえば仕方ないのですがその分しっかり自学でカバーしていきたいです。

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?