代码之家  ›  专栏  ›  技术社区  ›  Rob Gray

单元测试包含IDictionary的类

  •  0
  • Rob Gray  · 技术社区  · 15 年前

    我正在创建一个类,用于确定要调用的已注册WCF客户端回调的数量。当客户机向服务器注册时,它会提供它感兴趣的令牌。然后,该类将令牌到该令牌的IClientCallback接口的映射存储在字典中。

    public class Router
    {
        private IDictionary<int, IClientCallBack> clients;
    
        public Router()
        {
            clients = new Dictionary<int, IClientCallBack>();
        }
    
        public Router(IDictionary<int, IClientCallBack> clients)
        {
            this.clients = clients;
        }
    
        public bool Register(int token, IClientCallBack client)
        {
            if (!clients.ContainsKey(token))
            {
                clients.Add(token, client);
                return true;
            }
            return false;
        }
    }
    

    如何测试客户端是否成功注册到路由器? 我想我可以假设,如果函数返回true,那么它是成功的(但要阻止函数体仅为“return true;”?),或者我可以将客户机字典注入类的构造函数中,并在测试中检查clients.Count是否等于1,如下所示。

    [TestMethod]
    public void RegisterTest()
    {
        IDictionary<int, IClientCallBack> clients = new Dictionary<int, IClientCallBack>();
        var router = new Router(clients);
        var client = new Mock<IClientCallBack>().Object;
        var success = router.Register(4, client);
        Assert.IsTrue(success);
        Assert.AreEqual(1, clients.Count);
        Assert.AreEqual(clients[4], client);           
    }
    

    虽然上面的测试看起来不错,但使用依赖项注入来插入集合似乎有些过火,只是为了测试它。然而,它确实使测试变得更加简单和准确(当测试类的其他方法时)。

    这是测试该方法的推荐方法,还是过度杀伤力?

    3 回复  |  直到 15 年前
        1
  •  3
  •   Jon Skeet    15 年前

    那么,你为什么关心字典的内容呢?如果您已经注入了它,那么您可能会很在意——但在这一点上,它是API的一部分。如果不需要构造函数使用字典,那么只需测试实际的 影响 这本词典的内容。

    所以,如果为同一令牌调用Register两次,它应该返回false,对吗?做个测试。可能还有其他事情与令牌有关——所以测试令牌何时注册以及何时未注册。

    现在,在我开始听起来过于学术化之前,我不是那种认为单元测试应该总是,总是触及公共方法而不关心实现的人。它们不是黑盒测试。有时,了解实现可以使只测试一个复杂的逻辑位变得容易得多,即使它只是正常的 暴露的 通过一些实际做更多工作的方法。但在你介绍的案例中,我真的会坚持“它做了外界所能告诉它的事情”的方法。

        2
  •  0
  •   krosenvold    15 年前

    本质上,如果您正在查看内部数据结构,那么您的测试将检查特定的实现,而不是断言 预期行为

        3
  •  0
  •   Rob Gray    15 年前

    说得好,乔恩。上面的课程与我今天下午在工作中尝试的内容相比稍微简化了一点。在测试的实际代码中,我提供了1个或多个令牌来注册IClientCallBack。每个令牌都应该有一个字典条目。其想法是Resolve(int-token)将返回映射的IClientCallBack。。。

    如果我的方法是

    public bool Register(int token, IClientCallBack client)
    {
        return true;
    }
    

    我的测试会成功的。现在,我可以在类中包含对另一个方法的调用,以验证IClientCallBack是否正确映射,例如对Resolve的调用,但是我会在一个测试中测试不同的方法,我认为这是坏的因果报应?

    之前我刚刚测试过该方法会返回true,但是有声音告诉我,如果我将该方法编码为pass,它可能也不一定做我想做的事情。

    也许我只是弄乱了TDD点:)