【问题标题】:How to create unit tests for a fair distribution algorithm?如何为公平分配算法创建单元测试?
【发布时间】:2011-05-29 14:04:41
【问题描述】:

我有以下算法:
给定一个帐户列表,我必须将它们公平地分配给系统用户
现在,为了减轻用户的工作量,我不得不将他们分成几天。
因此,如果帐户有服务订单,则必须将它们插入到将在 547 天(一年半)内分发的列表中。如果帐户没有服务订单,则必须将它们插入到将在 45 天(一个半月)内分发的列表中。
我正在使用来自a question I asked before 的以下 LINQ 扩展:

public static IEnumerable<IGrouping<TBucket, TSource>> DistributeBy<TSource, TBucket>(
this IEnumerable<TSource> source, IList<TBucket> buckets)
{
    var tagged = source.Select((item,i) => new {item, tag = i % buckets.Count});
    var grouped = from t in tagged
                  group t.item by buckets[t.tag];
    return grouped;
}

我可以保证它有效。
我的问题是我真的不知道如何对所有这些情况进行单元测试。
我可能有 5 个用户和 2 个帐户,这可能不足以拆分一年半甚至一个半月。
我可能正好有 547 个帐户和 547 个系统用户,因此每个帐户每天将由每个系统用户处理。
基本上我不知道我应该创建什么样的数据集,因为似乎有太多的选择,我应该断言什么,因为我不知道分布将如何。

【问题讨论】:

    标签: c# unit-testing tdd nunit


    【解决方案1】:

    从边界条件(方法输入的自然限制)和任何已知的极端情况(方法以意外方式运行的地方,您需要特殊代码来解决这一问题)开始。

    例如:

    • 当帐户为零时,该方法的行为如何?
    • 零用户?
    • 只有一个帐户和用户
    • 547 个帐户和 547 个用户

    听起来您在这里已经了解了很多预期的边界条件。最初会很难想到极端情况,但在开发该方法时您可能会遇到其中的一些情况。它们也会自然而然地通过手动测试 - 每次您发现错误时,这是​​一个新的必要测试。

    一旦您测试了边界条件和极端情况,您还应该查看其他情况的“公平”示例 - 例如您的 5 个用户和 2 个帐户示例。您不能(或至少,可以说不需要)测试该方法的所有可能输入,但您可以测试一个有代表性的输入样本,例如账户分配不均。

    【讨论】:

    • 问题是我不知道输出会是什么。对于两个调用,输出可能相同,但可能完全不同。假设我有两个帐户 A 和 B,我还有两个系统用户 John 和 Bob。甚至不考虑他们是否有服务订单,只要将账户 A 分配给 John,将账户 B 分配给 Bob,则分配是公平的,反之亦然。那么如何检查不是幂等的呢?
    • @the_drow 我正要问这个问题,但后来我查看了算法,它看起来是确定性的。它真的需要是非确定性的吗?也就是说,随机性是业务需求吗?如果它必须是随机的,你可以模拟随机化器,让方法本身的行为与随机性隔离开来吗?
    • @the_drow 虽然我仍在努力查看两个输入相同的调用不会给出相同的输出。
    • @DavidHall:就我而言,这不是业务需求。然而,单元测试不应该关心实现细节,只关心预期的结果。如果引入一些随机性,允许测试中断可能是有效的妥协。如何判断算法是确定性的?
    • 所有这些关于赋值是否是确定性的讨论都暗示有一个隐含的概念等待被发现和明确化。也许作为可以注入 SUT 的 IAssignmentStrategy。
    【解决方案2】:

    我认为您的部分问题在于您的代码描述了您如何解决问题,而不是您要解决的问题。您当前的实现是确定性的,并为您提供了答案,但您可以轻松地将其与另一个“公平分配器”交换,这会给您一个细节上不同的答案(可能会为不同的用户分配不同的帐户),但满足相同的要求(分配是公平的)。
    一种方法是关注“公平”的含义。与其检查当前实现的确切输出,不如重新构建它,使其在高层次上看起来像:

    public interface IAllocator
    { IAllocation Solve(IEnumerable<User> users, IEnumerable<Account> accounts); }
    

    并编写测试来验证不是向用户执行帐户的具体实现,而是验证分配是否公平,有待定义 - 类似于“每个用户应该分配相同数量的帐户,正负一”。定义什么是公平,或者你的算法的确切目标是什么,应该可以帮助你确定感兴趣的极端情况。处理更高级别的目标(分配应该是公平的,而不是特定的分配)应该允许您轻松地交换实现并验证分配器是否在完成其工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-07
      • 2019-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多