@Sum001100

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

mockの使用方法について

解決したいこと

VB.netでWindowsフォームアプリケーションのユニットテスト内で、Moqライブラリを使いたいのですがうまく行きません。
引数でモックオブジェクトを渡すような方法は理解したのですが、テスト対象メソッドに引数がない場合どうしたら良いのでしょうか。

以下コードは質問用に作業中のコードを書き換えたものになっているため、意味のない継承やクラス定義が行われていますが、この条件のもとユニットテストを組みたいです。
また、ユニットテスト以外のコードは変更不可の状態です。

環境
.NETFramework 4.6.1
VisualStudioProfessional 2022

該当するソースコード

vb.net
Public Class Testtargetclass
    Inherits Mockclass0
    Dim value As String
    Friend Overrides Sub testtarget(ByRef value As String)
        Dim A As String
        value = mocktarget(A)
    End Sub
End Class

Public MustInherit Class Mockclass
    Friend Overridable Function mocktarget(A As String) As String
        A = “A”
        Return A
    End Function
End Class

Public MustInherit Class Mockclass0
    Inherits Mockclass
    Friend Overridable Sub testtarget(ByRef value As String)
        '何らかの処理
    End Sub
End Class

'以下テストクラス
<TestClass> Public Class Tclass
    Dim Testtargetclass As Testtargetclass = New Testtargetclass
    Dim mock As New Mock(Of Mockclass)
    Dim value As String
    <TestMethod>
    
    ‘ここでmocktargetメソッドがBを返すようなmockを作成し、testtargetメソッドを実行すると、valueがBになるようなコードがかきたい

    End Sub
End Class





0 likes

1Answer

サンプルコードのクラス名や関数名に日本語を使うのはできれば避けていただけませんか? 読みにくいと思ってる人が大多数だと思います。

「メソッドのmock化」と書いてありますが、実際にやりたいことは、

(1) ユニットテストを行いたい

(2) その際 Moq ライブラリを使いたい

・・・ですよね?


【追記】

質問者さんのやり方(消してしまった質問者さんのコード)では、Testtargetclass.testtarget(value) でモックを呼んでなくて、Testtargetclass のインスタンスメソッド mocktarget (Mockclass から継承したもので "A" を返す) を呼んでいるから、value = mocktarget(A) で value には "A" が代入され、Assert.AreEqual(“B”, value) は失敗したという当たり前の結果になったということです。

Testtargetclass の Mock オブジェクトを作って、Setup で testtarget(value) がモックの mocktarget を呼ぶように設定すれば Assert.AreEqual(“B”, value) は成功するはずです。検証してみましたので、どのようにしたのかを追記しておきます。

ユニットテストのプロジェクトは C# で作りました。以下の通りです。(自分は VB.NET は普段全く使ってないので、VB.NET では書けないというだけの理由です)

TestMethod6 が質問に書いてあった、

ここでmocktargetメソッドがBを返すようなmockを作成し、testtargetメソッドを実行すると、valueがBになるようなコードがかきたい

に該当します。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using WindowsApp1;

namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest1
    {
        // ・・・中略・・・

        // Moq で ref を持つメソッドの参照渡しの値をモック化する
        // https://qiita.com/wasimaru/items/4a3e5358eb64a7b2f3d8

        delegate void testtargetCallback(ref string value);

        [TestMethod]
        public void TestMethod5()
        {
            var mock = new Mock<Testtargetclass>(MockBehavior.Strict);
            mock.Setup(c => c.testtarget(ref It.Ref<string>.IsAny))
                .Callback(new testtargetCallback((ref string v) => v = "B"));
            string value = "Z";

            // 上の mock により value は "B" になるので Assert 成功
            mock.Object.testtarget(ref value);

            Assert.AreEqual("B", value);
        }

        [TestMethod]
        public void TestMethod6()
        {
            var mock = new Mock<Testtargetclass>(MockBehavior.Strict);
            mock.Setup(c => c.mocktarget(It.IsAny<string>())).Returns("B");
            mock.Setup(c => c.testtarget(ref It.Ref<string>.IsAny))
                .Callback(new testtargetCallback(
                    (ref string v) => 
                    {
                        string a = null;
                        v = mock.Object.mocktarget(a);
                    }
                )
            );
            string value = "Z";

            // 上の mock により value は "B" になるので Assert 成功
            mock.Object.testtarget(ref value);

            Assert.AreEqual("B", value);
        }
    }
}

Mockclass, Mockclass0, Testtargetclass のコードは質問欄の VB.NET のコードをコピペしました。ただし、アクセス修飾子が Friend ではモックが作れないので Public に変更しています。(VB.NET では Friend のままでモックが作れるのでしょうか?)

結果は:

result.jpg

1Like

Comments

  1. @Sum001100

    Questioner

    1. ユニットテストを行いたい

    (2) その際 Moq ライブラリを使いたい

    ・・・ですよね?

    すみません、その通りです。
    テスト対象のクラスと、モック対象のメソッドは書き換えずに、ユニットテストを組みたいのですが、うまくいかない状態です。

  2. ソース編集しました。

    コピペすると以下のようになります。

    code.jpg

    ソースを提示する場合はコピペすれば動くもの、少なくとも基本的な文法エラーのないものを貼ってください。

  3. @Sum001100

    Questioner

    すみません、かなり間違っていたので修正いたしました。

  4. 上に書いた、

    「メソッドのmock化」と書いてありますが、実際にやりたいことは、

    (1) ユニットテストを行いたい

    (2) その際 Moq ライブラリを使いたい

    ということに間違いなければ、それを質問欄を編集して追記してください。

    で、質問のコードの、

    Assert.AreEqual(B, value)
    ' →ここで、モック化したい元メソッドの戻り値Aが帰ってきてしまう
    

    の件ですが、上の (2) になってません。なので value が "A" になるのは当然の結果です。

  5. @Sum001100

    Questioner

    質問が正しくありませんでした。
    ご指摘いただいたように変更いたしました。

  6. 「上の (2) になってません」と言いましたが・・・

    話が通じてません。もっとよくその意味を考えてから返答してください。

  7. 表題の、

    引数のないメソッドで呼ばれるメソッドのmock化

    というのが意味不明なんですが、どういうことなんでしょう?

    そこはちょっと置いといて、とにかく Assert.AreEqual(“B”, value) が成功するようにしたいということなら、Testtargetclass クラスの Mock オブジェクトを作って、 Setup で testtarget(value) を参照渡しの value が "B" になるような Mock のコードを書かないとダメなはずですけど。

  8. @Sum001100

    Questioner

    (2)自体の書き方がわからない状況です。
     自分のコードがいかに見当違いなのかわかったので、質問文から削除しました。

    引数のないメソッドで呼ばれるメソッドのmock化
    →Test 対象メソッド(モックオブジェクトを引数に実行)
    という構造ではなく、
    Test対象メソッド()
    と実行すると、Test対象メソッド内で呼ばれるはずの別クラスのメソッドの代わりにモックが呼ばれる
    という状況を作りたかったのですが、よくわからない説明になってしまいました。

  9. 消してしまったコードでは、Mockclass の Mock オブジェクトを作って、Setup で mocktarget が "B" を返すようにして、Testtargetclass のインスタンスを作って、インスタンスメソッドの testtarget を呼んだら Mock の mocktarget が呼ばれると期待していたようですが、そんなことはできません。

  10. @Sum001100

    Questioner

    その通りです。そういう動きができるように書けないか、と思っていたのですが不可能なのですね、。
    Mocktargetをユニットテスト時にモック化する必要がある場合、そもそもモック化できるような書き方をする必要があるということになるということですね。
    ずっと考えていたのですごく助かりました。ありがとうございます。

  11. その通りです。そういう動きができるように書けないか、と思っていたのですが不可能なのですね

    「不可能」と言うことではなくてやり方の問題でしょう。

    質問者さんのやり方(消してしまった質問者さんのコード)では、Testtargetclass.testtarget(value) でモックを呼んでなくて、Testtargetclass のインスタンスメソッド mocktarget (Mockclass から継承したもので "A" を返す) を呼んでいるから、value = mocktarget(A) で value には "A" が代入され、Assert.AreEqual(“B”, value) は失敗したという当たり前の結果になったということです。

    Testtargetclass の Mock オブジェクトを作って、Setup で testtarget(value) がモックの mocktarget を呼ぶように設定すれば可能かもしれません。(思い付きレベルで未検証未確認です)

  12. 上のコメントに、

    Testtargetclass の Mock オブジェクトを作って、Setup で testtarget(value) がモックの mocktarget を呼ぶように設定すれば可能かもしれません。(思い付きレベルで未検証未確認です)

    と書きましたが、検証してみましたので、後で回答欄にどのようにしたのかを追記しておきます。

Your answer might help someone💌