简体   繁体   English

你将如何对内存分配器进行单元测试?

[英]How would you unittest a memory allocator?

There's a lot of people today who sell unittesting as bread-and-butter of development. 今天有很多人将单元测试作为发展的基础。 That might even work for strongly algorithmically-oriented routines. 这甚至可能适用于强大的面向算法的例程。 However, how would you unit-test, for example, a memory allocator (think malloc()/realloc()/free()). 但是,如何进行单元测试,例如内存分配器(想想malloc()/ realloc()/ free())。 It's not hard to produce a working (but absolutely useless) memory allocator that satisfies the specified interface. 生成满足指定接口的工作(但绝对无用)的内存分配器并不难。 But how to provide the proper context for unit-testing functionality that is absolutely desired, yet not part of the contract: coalescing free blocks, reusing free blocks on next allocations, returning excess free memory to the system, asserting that the allocation policy (eg first-fit) really is respected, etc. 但是如何提供绝对需要的单元测试功能的适当上下文,但不是合同的一部分:合并空闲块,在下一次分配时重用空闲块,将过多的空闲内存返回给系统,断言分配策略(例如第一次合适)真的受到尊重等

My experience is that assertions, even if complex and time-consuming (eg traversing the whole free list to check invariants) are much less work and are more reliable than unit-testing, esp. 我的经验是断言,即使复杂和耗时(例如遍历整个空闲列表以检查不变量),工作要少得多,并且比单元测试更可靠,尤其是。 when coding complex, time-dependent algorithms. 编码复杂的,时间相关的算法。

Any thoughts? 有什么想法吗?

Highly testable code tends to be structured differently than other code. 高度可测试的代码往往与其他代码的结构不同。

You describe several tasks that you want an allocator to do: 您描述了您希望分配器执行的几个任务:

  • coalescing free blocks 合并自由块
  • reusing free blocks on next allocations 在下次分配时重用空闲块
  • returning excess free memory to the system 将多余的可用内存返回给系统
  • asserting that the allocation policy (eg first-fit) really is respected 断言分配政策(例如先适合)确实得到尊重

While you might write your allocation code to be very coupled, as in doing several of those things inside one function body, you could also break each task out into code that is a testable chunk. 虽然您可能会将分配代码编写为非常耦合,例如在一个函数体内执行其中的一些操作,但您也可以将每个任务分解为可测试块的代码。 This is almost an inversion of what you may be used to. 这几乎是你可能习惯的倒置。 I find that testable code tends to be very transparent and built from more small pieces. 我发现可测试代码往往非常透明,并且由更小的部分构建。

Next, I would say is that within reason automated testing of any sort is better than no automated testing. 接下来,我想说的是,在任何类型的自动化测试中,优于没有自动化测试。 I would definitely focus more on making sure your tests do something useful than worrying if you've properly used mocks, whether you've ensured it's properly isolated and whether it's a true unit test. 我肯定会更专注于确保你的测试做一些有用的事情而不是担心如果你正确使用了模拟,你是否确保它被正确隔离以及它是否是真正的单元测试。 Those are all admirable goals that will hopefully make 99% of tests better. 这些都是令人钦佩的目标,有望使99%的测试更好。 On the other hand, please use common sense and your best engineering judgment to get the job done. 另一方面,请使用常识和最佳工程判断来完成工作。

Without code samples I don't think I can be more specific. 没有代码示例我不认为我可以更具体。

If there is any logic in there, it can be unit-tested. 如果那里有任何逻辑,它可以进行单元测试。
If your logic involves making decisions and calling OS/hardware/system APIs, fake/mock out the device dependent calls and unit test your logic to verify if the right decisions are made under a given set of pre-conditions. 如果您的逻辑涉及做出决策并调用OS /硬件/系统API,则伪造/模拟设备相关的调用并对您的逻辑进行单元测试,以验证是否在给定的一组前置条件下做出了正确的决策。 Follow the Arrange-Act-Assert triad in your unit test. 在单元测试中遵循Arrange-Act-Assert三元组。
Assertions are no replacement for automated unit-tests. 断言不能替代自动化单元测试。 They dont tell you which scenario failed, they don't provide feedback during development, they can't be used to prove that all specs are being met by the code among other things. 他们不告诉你哪个场景失败了,他们在开发过程中不提供反馈,他们不能用来证明代码满足所有规范。

Non-vague Update: I don't know the exact method calls.. I think I'll 'roll my own' Let's say your code examines current conditions, makes a decision and make calls to the OS as required. 非模糊更新:我不知道确切的方法调用..我想我会'自己动手'让我们说你的代码检查当前条件,做出决定并根据需要调用操作系统。 Lets say your OS calls are (you may have many more): 让我们说你的操作系统调用(你可能有更多):

void* AllocateMemory(int size); 
bool FreeMemory(void* handle);
int MemoryAvailable();

First turn this into an interface, I_OS_MemoryFacade . 首先将其转换为I_OS_MemoryFacade接口。 Create an implementation of this interface to make the actual calls to the OS. 创建此接口的实现以实际调用OS。 Now make your code use this interface - you now have decoupled your code/logic from the device/OS. 现在让您的代码使用此接口 - 您现在已将代码/逻辑与设备/操作系统分离。 Next in your unit test, you use a mock framework (its purpose is to give you a mock implementation of a specified interface. You can then tell the mock framework that expect these calls to be made, with these params and return this when they are made. At the end of the test, you can ask the mock framework to verify if all the expectations were met. (eg In this test, AllocateMemory should be called thrice with 10, 30, 50 as params followed by 3 FreeMemory calls. Check if MemoryAvailable returns the initial value.) 接下来在你的单元测试中,你使用一个模拟框架(它的目的是给你一个指定接口的模拟实现。然后你可以告诉模拟框架, 期望这些调用,使用这些参数,并在它们是时返回在测试结束时,您可以要求模拟框架验证是否满足所有期望。(例如,在此测试中,AllocateMemory应被调用三次,10,30,50作为参数,然后是3个FreeMemory调用。如果MemoryAvailable返回初始值。)
Since your code depends on an interface, it doesn't know the difference between the real implementation and a fake/mock implementation that you use for testing. 由于您的代码依赖于接口,因此它不知道实际实现与您用于测试的伪/模拟实现之间的区别。 Google out 'mock frameworks' for more information on this. Google提供了“模拟框架”以获取更多相关信息。

Both things have their place. 这两件事都有自己的位置。 Use unit tests to check the interfaces behave as expected and assertions to check that the contract is respected. 使用单元测试检查接口的行为是否符合预期,并使用断言来检查合同是否得到遵守。

You may also want to include performance testing, stress testing etc. They wouldn't be unit tests, because it would test entire thing, but they're very valuable in case of memory allocator. 您可能还希望包括性能测试, 压力测试等。它们不是单元测试,因为它会测试整个事情,但它们在内存分配器的情况下非常有价值。

Unit testing does not exclude these kinds of tests. 单元测试不排除这些类型的测试。 It's best to have both of them. 最好同时拥有它们。

I also think unit tests are overrated. 我也认为单元测试被高估了。 They have their usefulness, but what really increases the quality of a program is to review it. 它们有用,但真正提高程序质量的是审查它。 On the other hand I'm really fond of assertions, but they don't replace unit testing. 另一方面,我真的喜欢断言,但他们不会取代单元测试。

I'm not talking about peer-review, but simply reread what you wrote, possibly while stepping through it with a debugger and checking that each line does what it's supposed to do will sky rocket software quality. 我不是在谈论同行评审,而是简单地重读你所写的内容,可能是在用调试器踩过它并检查每一行是否完成预期的天空火箭软件质量时。

I'd recommend "high level" unit tests that test a chunk of functionality rather than a tiny method call. 我建议使用“高级”单元测试来测试一大块功能而不是一个小方法调用。 The later tend to make any code change extremely painful and expensive. 后者倾向于使任何代码更改非常痛苦和昂贵。

Personally I find most of the unit tests like someone else's desire rather than mine. 就个人而言,我发现大多数单元测试都像别人的愿望而不是我的。 I think that any unit test should be written just like a normal program except the fact that it doesn't do anything other than testing a library/algorithm or any part of code. 我认为任何单元测试都应该像普通程序一样编写,除了它除了测试库/算法或代码的任何部分之外什么都不做。

My unit tests usually don't use tools like CUnit , CppUnit and similar software. 我的单元测试通常不使用CUnitCppUnit和类似软件之类的工具 I create my own tests. 我创建自己的测试。 For example not long ago I needed to test a fresh implementation of a container in usual cases for memory leaks. 例如,不久前我需要在通常情况下测试容器的新实现是否存在内存泄漏。 A unit test was not helpful enough for providing a nice test. 单元测试对提供一个不错的测试没有帮助。 Instead I created my own allocator and made it fail to allocate memory after a certain (adjustable) number of allocations to see if my application has memory leaks in these case ( and it had :) ). 相反,我创建了自己的分配器,并且在某些(可调整的)分配数量之后无法分配内存,以查看我的应用程序是否在这些情况下有内存泄漏(并且它有:))。

How can this be made by a unit test? 如何通过单元测试来做到这一点? With more effort to make your code fit into the unit test "pattern". 通过更多努力使您的代码适合单元测试“模式”。

So I strongly recommend not using unit test every time just because it is "trendy", but only when it is really easy to integrate them with the code you like to test. 因此我强烈建议不要每次都使用单元测试,因为它是“时髦的”,但只有在将它们与您要测试的代码集成时才非常容易。

Unit testing isn't just to make sure your code works. 单元测试不仅仅是为了确保您的代码有效。 It is also a very good design methodology. 它也是一种非常好的设计方法。 For tests to be useful, as mentioned previously, the code needs to be as decoupled as possible, such as using interfaces where needed. 要使测试有用,如前所述,代码需要尽可能地解耦,例如在需要时使用接口。

I don't always write tests first, but very often if I am having trouble getting started on something, I will write a simple test, experiment with the design and go from there. 我并不总是首先编写测试,但是如果我在开始学习时遇到困难,我会写一个简单的测试,试验设计并从那里开始。 Also, good unit tests serve as good documentation. 此外,良好的单元测试可作为良好的文档。 At work when I need to see how to use a specific class or similar, I look at the unit tests for it. 在我需要查看如何使用特定类或类似的工作时,我会查看它的单元测试。

Just remember that Unit Testing is not integration testing. 请记住,单元测试不是集成测试。 Unit testing does have its limits but overall I think a very good tool to know how to use appropriately. 单元测试确实有其局限性但总体而言我认为这是一个非常好的工具,可以帮助您了解如何正确使

So you run into a problem that your allocator gets used by the testing framework which may cause problems for the state of your allocator while you're trying to test. 所以你遇到一个问题,你的分配器被测试框架使用,这可能会在你试图测试时导致你的分配器状态出现问题。 Consider prefixing your allocator functions (see dlmalloc ). 考虑为分配器函数添加前缀(请参阅dlmalloc )。 You write 你写

prefix_malloc();
prefix_free();

and then 然后

#ifndef USE_PREFIX
#define prefix_malloc malloc
#define prefix_free free
#endif

Now, set your build system to compile a version of the library using -DUSE_PREFIX. 现在,设置构建系统以使用-DUSE_PREFIX编译库的版本。 Write your unit tests to call prefix_malloc and prefix_free. 编写单元测试以调用prefix_malloc和prefix_free。 This lets you separate your allocator's state from the state of the system allocator. 这使您可以将分配器的状态与系统分配器的状态分开。

If you're using sbrk and the system allocator uses sbrk it's possible you could have a bad time if either allocator assumes it has complete control over the breakpoint. 如果您正在使用sbrk并且系统分配器使用sbrk,那么如果任一分配器假定它完全控制断点,则可能会遇到错误的时间。 In this case you would want to link in yet another allocator which you can configure to only use mmap so your allocator can have the breakpoint. 在这种情况下,您可能希望链接另一个分配器,您可以将其配置为仅使用mmap,以便分配器可以具有断点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM