簡體   English   中英

如何使用Moq測試方法之間的依賴關系?

[英]How should I test dependencies between methods using Moq?

我想確保正在測試的方法使用某些參數的相同值對模擬方法進行兩次調用。 這些值是在要測試的方法內部生成的,因此在設置測試時未知。 在這種情況下,被測試的方法是在Redis中存儲兩個相關的項目,但這與問題無關。 這是我要問的嘲笑。 我想確認的是,完成工作的方式是執行此操作的最佳方法。 可能我還沒有想過Moq的其他功能,可以讓它以更好的方式完成。

這就是我所擁有的。

IRedisClient _redisClientMock = new Mock<IRedisClient>(MockBehavior.Strict);

string token = null;
DateTime expires = default(DateTime);
_redisClientMock
    .Setup(x => x.Set(KEY_PREFIX, It.IsAny<string>(), $"{TEST_ID},{TEST_NAME}", It.IsAny<DateTime>()))
    .Callback<string, string, string, DateTime?>((p, k, v, e) =>
    {
        token = k;
        expires = e.Value;
    });
_redisClientMock
    .Setup(x => x.Set(KEY_PREFIX, TEST_ID, It.IsAny<string>(), It.IsAny<DateTime>()))
    .Callback<string, string, string, DateTime?>((p, k, v, e) =>
    {
        if (!v.Equals(token, StringComparison.InvariantCultureIgnoreCase))
            throw new Exception($"RedisClient.Set expected value {token} but received {v}");
        if (!e.Value.Equals(expires))
            throw new Exception($"RedisClient.Set expected expires {expires} but received {e}");
    });

我不得不在回調中使用異常的事實似乎有點笨拙,因此我想知道是否有更好的方法可以通過模擬來驗證這一點。

這是被測試代碼的示例,這是由實際測試的方法調用的私有方法。

string StoreClientDetailsInRedisAndReturnToken(string clientId, string clientName)
{
    string token = Guid.NewGuid().ToString("N");
    string data = $"{clientId},{clientName}";
    DateTime expires = DateTime.UtcNow.AddDays(AdminSettings.Current.ExpiryDays);
    RedisClient.Set(KeyPrefix, token, data, expires);
    RedisClient.Set(KeyPrefix, clientId, token, expires);
    return token;
}

令牌包含在通過電子郵件發送的激活鏈接中,因此我們需要能夠使用令牌檢索數據。 第二個Redis集允許我們也使用clientId檢索令牌,以確認請求仍在等待處理中。 即,該鏈接尚未被單擊和處理,並且Redis條目尚未過期並已被刪除。

我確定您會建議其他方法來編寫受測代碼,但是編寫此代碼后,我已經可以想到其他方法來編寫它。 從這個問題中我真正想知道的是,Moq是否允許一個人驗證以某種方式關聯的兩種方法都以特定順序調用。

這就是我將如何測試此方法。 這一切都非常簡單明了,並且可以對您在代碼中指定的所有行為進行斷言。

[TestMethod]
public void StoreClientDetailsInRedisAndReturnToken_SetsTwoValuesInRedis_ReturnsGuid()
{
  var data = new List<dynamic>()
  var redis = new Mock<IRedisClient>();
  redis.Setup(x => x.Set(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<DateTime>()).Callback((string kp, string token, string data, DateTime exp) => data.Add(new {KeyPrefix = kp, Key = token, Data = data, Expiry = exp);
  var target = new MyClass(redis.Object);

  var result = target.StoreClientDetailsInRedisAndReturnToken("funny", "banana");

  new Guid(result); // do nothing, will throw if result is not parseable as Guid

  Assert.AreEqual(2, data.Count())
  Assert.AreEqual(KeyPrefix, data.FirstOrDefault().KeyPrefix);
  Assert.AreEqual(result, data.FirstOrDefault().Key);
  Assert.AreEqual("funny,banana", data.FirstOrDefault().Data);
  // here if you have enterprise edition of visual studio you can use microsoft fakes to actually test it, or otherwise
  Assert.IsTrue(((DateTime)data.FirstOrDefault().Expiry) > DateTime.UtcNow.AddDays(AdminSettings.Current.ExpiryDays).AddMinutes(-1));

  Assert.AreEqual(KeyPrefix, data.LastOrDefault().KeyPrefix);
  Assert.AreEqual("funny", data.LastOrDefault().Key);
  Assert.AreEqual(result, data.LastOrDefault().Data);
  Assert.IsTrue(((DateTime)data.LastOrDefault().Expiry) > DateTime.UtcNow.AddDays(AdminSettings.Current.ExpiryDays).AddMinutes(-1));
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM