0

我正在用 C# 编写内存缓存(因为没有更好的术语)。编写缓存是容易的部分,但测试它是另一个......

我对使用定时器的测试类的所有研究都是模拟定时器并将其注入到类中。有了这个,需要为每个添加到缓存的订单初始化一个新的计时器。将计时器传递给 Add 函数可以解决这个问题,但使用 OrderCache 的类不应该负责将计时器传递给它。

我需要验证

  • 定时器已初始化
  • 在指定的持续时间后以正确的顺序调用 Remove
  • 订单在字典中更新,两次添加相同订单时不会创建计时器

    private readonly Dictionary<int, Order> _orders;
    
    private TimeSpan _cacheDuration;
    
    public OrderCache(TimeSpan cacheDuration)
    {
        _cacheDuration = cacheDuration;
        _orders = new Dictionary<int, Order>();
    }
    
    public void Add(Order order)
    {
        var cachedOrder = GetOrderById(order.Id);
        if (cachedOrder == null)
        {
            _orders.Add(order.Id, order);
            var timer = new Timer(_cacheDuration.TotalMilliseconds);
            timer.Elapsed += (sender, args) => Remove(order.Id);
        }
        else
        {
            _orders[order.Id] = order;
        }
    }
    
    public Order GetOrderById(int orderId)
    {
        return _orders.ContainsKey(orderId) ? _orders[orderId] : null;
    }
    
    public void Remove(int orderId)
    {
        if (_orders.ContainsKey(orderId))
        {
            _orders.Remove(orderId);
        }
    }
    
4

2 回答 2

1

A:定时器已经初始化

问:你应该避免白盒测试,因为如果内部实现发生了变化,而不是行为、接口或契约,你应该重新重写测试。

A:在指定的持续时间后,以正确的顺序调用 Remove

问:你可以写测试:

[Test]
void Should_remove_...()
{
    MockTimer timer = new MockTimer();    

    MyCache cache = new MyCache(timer);
    DateTime expiredAt = DateTime.Now.Add(..);
    cache.Add("key", "value", expiredAt);

    timer.SetTime(expiredAt);

    Assert.That(cache, Is.Empty)
}

PS:我推荐使用http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.aspx

于 2013-07-19T14:30:26.533 回答
1

话虽如此:)

计算机科学中只有两个难点:缓存失效和命名。

——菲尔·卡尔顿

@JonSkeet 的建议是要走的路——要么在每次轮询时清理缓存(如果经常发生),要么有一个线程定期清理。

如果您仍然认为保留您的方法,而不是将计时器传递到缓存中,请传递ITimerFactory. 然后每次Add调用都可以使用工厂创建一个计时器,并进行相应的设置。

您可以模拟工厂以生成模拟计时器并将其设置为适当的时间。你可以控制你的测试。

您的测试将具有以下形式(伪):

var timer = new Mock<ITimer>();
var factory = new Mock<ITimerFactory>();
factory.Setup(f => f.CreateTimer()).Returns(timer.Object);
....
....
timer.Verify(t => t.SetTime(expiredAt), Times.Once());
于 2013-07-19T15:08:49.710 回答