单元测试和断言方法的内部

时间:2014-08-17 15:59:24

标签: c# unit-testing caching

我正在尝试使用单元测试来测试一个方法,但是也要确保它在内部以预期的方式执行。这是简化的方法;它从数据库返回一个值,但也将其保存到缓存中,因此如果在5秒内再次请求它,则将从缓存而不是数据库中检索该值。

public static string GetValue()
{
    var cache = HttpRuntime.Cache;
    string value = (string)cache["test"];

    // if value is null then it was never in cache or it expired.
    if (value == null)
    {
        // Imagine here complex code that retrieves and sets "value"
        value = "OK";

        // Add it to cache to retrieve it faster if requested again within 5 sec.
        cache.Add("test", value, null, DateTime.Now + TimeSpan.FromSeconds(5),
                System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, null);

        Debug.WriteLine("From DB");
    }
    else
    {
        // Value was in cache, so it's ready to return
        Debug.WriteLine("From Cache");
    }

    return value;
}

这基本上从假设数据库返回值“OK”。但是,因为它使用HttpRuntime.Cache的绝对过期,如果在5秒内再次请求该值,则从Cache返回,而不是从DB返回。

现在我的问题是,如何编写一个TestMethod,它不仅验证它返回OK,而且还有缓存逻辑正常工作。

请注意,根据它是使用DB还是Cache来获取值,会在调试输出中添加相应的调试行。

所以测试方法应该是这样的:

[TestMethod]
public void GetValue_OK()
{
    Assert.IsTrue(Helpers.GetValue() == "OK");

    Thread.Sleep(4000);
    Assert.IsTrue(Helpers.GetValue() == "OK");

    // Assert that it wrote "From Cache"
    Assert.IsTrue(LastDebugLine().Contains("Cache"));

    Thread.Sleep(2000);
    Assert.IsTrue(Helpers.GetValue() == "OK");

    // Assert that it wrote "From DB", because theoretically over 5 seconds passed
    // So it has expired and the routine loaded it again from the DB.
    Assert.IsTrue(LastDebugLine().Contains("DB"));
}

string LastDebugLine()
{
    // Imagine a string named LastDebugOutput that contains the 
    // last line of output from Debug.WriteLine
    // The code below retrieves somehow this line.
    return String.Empty;
}

在testmethod中,我不仅验证了正确的输出,还验证了它是从缓存还是数据库中检索出来的。我运行该方法3次,在每次检索之间插入延迟。我还想测试是否从数据库或缓存中检索该值。

我认为这可能发生的方式是使用一种方法,我调用LastDebugLine()从被测试的方法中检索最后一个调试信息。通过读取它的状态,测试方法知道方法的内部,因此可以与预期结果进行比较。

现在我的问题有两个部分:

1)测试所有这些的正确方法是什么?我的想法是使用调试输出并在单元测试中检查它是否正确?我在这里对于一般概念可能是非常错误的,所以也许这可以做得更好。

2)但是,如果我的概念是正确的,那么LastDebugLine()中的代码到底应该从Debug.WriteLine获取最后一行呢?

即使我是正确的,仍然存在问题,因为单元测试可能会运行多线程,因此读取这样的调试输出可能会带来意想不到的结果。

如何正确测试此方法?

1 个答案:

答案 0 :(得分:1)

有几种方法可以解决这个问题。要完成整个测试,你可以

  • 创建一个测试,调用该方法将值放入DB / Cache。
  • 然后我会让测试删除db
  • 中的值
  • 然后再次调用该方法。

如果从缓存中获取值,那么您将获得正确的值。如果方法调用db,则测试将失败。

作为替代方案,您可以隔离访问db和缓存的组件,并将执行此操作的对象注入到测试的类中。然后,您可以在测试中提供模拟,以断言以正确的顺序调用对象。

在你目前的情况下,由于你有一个静态方法很困难,所以注入依赖很难,但如果你改变你的GetValue方法不是静态的,你可以传递一些包装你的HttpRuntime.Cache和实现一个类似的接口,然后传递该对象的模拟,并验证当缓存中的对象不存在时,其他代码被调用,当它在缓存中存在时,它不会被调用

如果您无法更改模型以使GetValue不是静态的,那么您可以查看使用MS Fakes框架或其他可以模拟静态方法的商业产品。