【问题标题】:C# + NUnit: Unit testing methods with byte array argumentsC# + NUnit:带有字节数组参数的单元测试方法
【发布时间】:2013-08-28 18:53:52
【问题描述】:

我想为一些带有字节数组参数的类编写单元测试。 总共有大约 100 个方法,数组大小从 5-10 到几百个字节不等。 我应该如何生成和存储测试数组?

  1. 我应该手动生成它们还是通过一些生成器代码(也应该进行单元测试)?
  2. 我应该在测试期间在内存中生成它们,还是应该提前生成它们并将它们存储在某个地方?
  3. 在后一种情况下,我应该将它们存储在文件中(即使单元测试不应该触及文件系统),还是应该将它们存储在测试代码本身中(例如,以十六进制格式的字符串,像这样:“47 08 00 14 等”)?

我开始手动创建它们并将它们以十六进制字符串的形式存储在测试代码中。我用这样的二进制字符串做了很多工作,所以我可以相对容易地阅读它们(“我什至没有看到代码。我看到的只是金发、黑发、红发。”)问题是,这种方法很慢,而且我认为使用自动生成器会导致更易于维护的测试。但是我应该如何测试生成器的输出是否正确?听起来像第 22 条军规……

【问题讨论】:

  • But how should I test that the output of the generator is correct? Sounds like Catch-22... 以及如何测试您的测试是否真的在测试他们需要测试的内容? :-) 在某个时刻,你必须信任和希望 :-) :-)
  • @xanatos 当然。但在这种特殊情况下,这一点在哪里?你有什么建议?
  • 这可能是一个愚蠢的问题,但是对这样的生成器进行单元测试有什么问题?
  • @Halvard 生成器的方法将使用其用户友好的参数(布尔值、整数、字符串等)创建给定结构的字节数组。在这种方法的单元测试中,您给出一些参数,并断言输出是所需的字节数组。但是如何创建和存储这些字节数组呢?这是同样的问题。唯一的区别是,在原始情况下,字节数组是测试方法的输入——在生成器的情况下,它们是输出
  • 您能否展示并解释 100 种方法中的一种?它可能会更好地说明您要实现的目标。

标签: c# unit-testing nunit bytearray hex


【解决方案1】:


我会建议一些方法,但不确定哪种方法适合或什至可能适合您,因为在您的案例中没有您使用的方法的示例。
如果您可以提供您的方法的示例并解释其工作原理,那将会很有用。你的 100 多种方法有什么作用?

1)尝试将100多种方法中与字节数组一起使用的逻辑重构并提取到一些辅助方法中。这样,辅助方法可以处理字节数组,然后将反序列化的对象传递给 100 个奇怪的方法,这些方法现在将开始使用对象进行处理。



2) 就我个人而言,我更喜欢将字节数组放在测试方法旁边的代码中,这是您当前采用的方法。也许一些输入组合作为函数的属性(使用 NUnit RowTest 扩展)。这使得单元测试相对独立于其他测试。如果您更改参数,那么只有特定的单元测试会受到影响。
生成器的最大问题是它可能会变得非常复杂,因为它为 100 个奇怪的方法中的每一个返回字节数组的逻辑。对生成器的更改可能会影响您的所有单元测试。

单元测试应该易于维护和独立。通过引入生成器,您正在添加依赖项并增加复杂性。

干杯!

【讨论】:

  • +1 用于提及 RowTest 扩展!我将尝试为 NUnit 编写一个 Attribute,它与 TestCaseAttribute(RowTest 的继承者)非常相似,但它能够接受我的十六进制字符串并将它们转换为byte[]
【解决方案2】:

我建议使用 Microsoft 的 Text Templates 作为单元测试的生成器。只需将 .tt 文件添加到您的项目并实现生成器。生成在设计时完成(当您保存或更改模板时)并在您的项目中生成一个 .cs 文件。然后可以将您的测试代码作为普通的“手写”代码处理,每个字节数组和方法进行一次测试。

生成器还可以为您的字节数组和预期输出加载定义文件。但是由于它是在设计时完成的,因此您的单元测试没有使用文件系统。

整个T4系统真的值得一看:Design-Time Code generation by using T4 Text Templates

...默认情况下它是 VisualStudio 的一部分 - 无需安装任何东西或更改构建脚本。

【讨论】:

  • 谢谢,听起来是个有趣的主意。我以前从未使用过 TT,所以我不确定它们是否能提供帮助,但我一定会尝试的。
【解决方案3】:

您可以使用生成器代码来获取每个测试的字节[]。例如,您可以在 Test 类中使用一个方法来生成具有随机大小的 byte[]。像这样的

byte[] GetRandomBytes()
{ 
Random random = new Random();
int randomNumber = random.Next(0, 100);
List<byte> bytes = new List<byte>();
for(int b = 0; b < randomNumber; b++)
    bytes.Add(b);
return bytes.ToArray();
}

生成器代码也将通过您的实际测试进行测试,因为它还应该处理无效输入案例(我假设)

【讨论】:

  • 谢谢,但正如我在上面的 cmets 中所说,数组具有预定义的结构。
  • 这是一个非常糟糕的主意,因为它是非确定性的,这意味着测试可能会在一段时间内失败并可能通过另一次。
【解决方案4】:

我假设您希望字节实际代表可反序列化的对象,而不仅仅是随机的。 一百个字节左右将构成一个小的 base64 编码字符串。您可以将测试输入保存为 base 64 编码字符串,然后测试可以获取正确的输入并将其转换为字节:

    const string someScenario = 
"R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==";

    byte[] bytes = Convert.FromBase64String(someScenario);

你可以通过你的序列化器提前知道字符串是什么,比如

public string SerializeAsBase64()
{
    var session = new SessionCredentials { SessionKey = Guid.NewGuid() };

    using (var mem = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(mem, session);

        var bytes = mem.ToArray();

        return Convert.ToBase64String(bytes);
    }
}

为了确保它可以正常工作,您是否还有反序列化器...?序列化然后反序列化然后比较原件是否相等?

【讨论】:

  • +1 感谢您的工作,但我认为我的十六进制字符串比它们的 bas64 编码等效项更具可读性和更容易修改。
猜你喜欢
  • 2021-08-25
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多