69

Moq でメソッドを 2 回設定したいのですが、最後のメソッドが前のメソッドをオーバーライドしているようです。ここに私の初期設定があります:

string username = "foo";
string password = "bar";

var principal = new GenericPrincipal(
    new GenericIdentity(username),
    new[] { "Admin" });

var membershipServiceMock = new Mock<IMembershipService>();
membershipServiceMock.Setup(ms =>
    ms.ValidateUser(username, password)
).Returns(new ValidUserContext { 
    Principal = principal
});

これはうまくいきnew ValidUserContext()ますが、ユーザー名またはパスワードが上記の変数usernameと異なる場合はこれを返したいと思います。passwordそのために、別のセットアップを追加しましたが、今回は上記のセットアップをオーバーライドし、常に適用します。

membershipServiceMock.Setup(ms =>
    ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>())
).Returns(
    new ValidUserContext()
);

Moqでこの種の状況を処理する最もエレガントな方法は何ですか?

編集

以下のアプローチで問題を解決しましたが、これを処理するより良い方法があると思います:

var membershipServiceMock = new Mock<IMembershipService>();
membershipServiceMock.Setup(ms =>
    ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>())
).Returns<string, string>((u, p) => 
    (u == username && p == password) ?
    new ValidUserContext { 
        Principal = principal
    }
    : new ValidUserContext()
);
4

3 に答える 3

90

Moq は、引数の制約を使用してこれをすぐにサポートします。

mock.Setup(ms => ms.ValidateUser(
        It.Is<string>(u => u == username), It.Is<string>(p => p == password))
    .Returns(new ValidUserContext { Principal = principal });
mock.Setup(ms => ms.ValidateUser(
        It.Is<string>(u => u != username), It.Is<string>(p => p != password))
    .Returns(new ValidUserContext());

キャッチオール It.IsAnyも機能しますが、順序が重要です。

// general constraint first so that it doesn't overwrite more specific ones
mock.Setup(ms => ms.ValidateUser(
        It.IsAny<string>(), It.IsAny<string>())
    .Returns(new ValidUserContext());
mock.Setup(ms => ms.ValidateUser(
        It.Is<string>(u => u == username), It.Is<string>(p => p == password))
    .Returns(new ValidUserContext { Principal = principal });
于 2012-10-13T10:38:03.683 に答える
5

もう 1 つの標準オプションは、Return<> バージョンを使用して、パラメーターに応じて異なる ValidUserContexts を返すことです。上記の回答よりも優れているわけではなく、別のオプションです。

関数 GetUserContext(string, string) の結果を返すように ValidateUser() を設定し、ValidateUser() が呼び出されたユーザー名とパスワードを渡します。

[TestClass]
public class MultipleReturnValues {

    public class ValidUserContext {
        public string Principal { get; set; }
    }

    public interface IMembershipService {
        ValidUserContext ValidateUser(string name, string password);
    }

    [TestMethod]
    public void DifferentPricipals() {

        var mock = new Mock<IMembershipService>();
        mock.Setup(mk => mk.ValidateUser(It.IsAny<string>(), It.IsAny<string>())).Returns<string, string>(GetUserContext);

        var validUserContext = mock.Object.ValidateUser("abc", "cde");

        Assert.IsNull(validUserContext.Principal);


        validUserContext = mock.Object.ValidateUser("foo", "bar");

        Assert.AreEqual(sPrincipal, validUserContext.Principal);


    }

    private static string sPrincipal = "A Principal";
    private static ValidUserContext GetUserContext(string name, string password) {

        var ret = new ValidUserContext();

        if (name == "foo" && password == "bar") {
            ret = new ValidUserContext { Principal = sPrincipal };
        }
        return ret;

    }
}
于 2012-10-17T08:44:04.880 に答える