繁体   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