简体   繁体   English

单元测试如何测试?

[英]How is unit testing testing anything?

I don't understand how I'm testing anything with unit testing. 我不明白我如何使用单元测试来测试任何东西。

Suppose I am testing that my repository class can retrieve values from the database correctly. 假设我正在测试我的存储库类可以正确地从数据库检索值。 The proper way to do this would be to actually call the real database and retrieve and check those values. 正确的方法是实际调用真实数据库并检索并检查这些值。

But the idea behind unit testing is that it should be done in isolation, and connecting to a running database is not isolation. 但是单元测试背后的想法是,它应该孤立地完成,并且连接到正在运行的数据库不是孤立的。 So what is usually done is to mock or stub the database. 因此,通常要做的是模拟存根数据库。

But why would testing on a fake database with hardcoded data and hardcoded return values even test anything? 但是,为什么要对具有硬编码数据和硬编码返回值的假数据库进行测试,甚至可以测试任何东西? It seems tautological and a waste of time. 似乎是重言式和浪费时间。

Or am I not understanding how to unit test properly? 还是我不了解如何正确进行单元测试?

Does one even unit test database calls? 是否可以对数据库调用进行单元测试?

I don't understand how I'm testing anything with unit testing. 我不明白我如何使用单元测试来测试任何东西。

Short answer: you are testing the logic, and leaving out the side effects . 简短的答案:您正在测试逻辑, 并排除了副作用

You aren't testing everything ; 您并未测试所有内容 ; but you are testing something. 但是您正在测试某些东西。

Furthermore, if you keep in mind that you aren't really testing the code with side effects, then you are motivated to arrange your code so that the pieces that actually depend on the side effect are small. 此外,如果您牢记您并没有真正在测试带有副作用的代码,那么您就有动机去安排代码,以使实际依赖于副作用的部分很小。 The big pieces don't actually care where the data comes from, to those are easy to test. 大型组件实际上并不关心数据的来源,因为它们很容易测试。

So "something" can be "most things". 因此,“某物”可以是“大多数东西”。

There is an impedance problem -- if your test doubles impersonate the production originals inadequately, then some of your test results will be inaccurate. 一个阻抗问题-如果您的测试双打冒充生产原件不足,那么你的一些测试结果将是不准确的。

my philosophy is to test as little as possible to reach a given level of confidence 我的理念是尽可能少地测试以达到给定的置信度

Kent Beck, 2008 肯特·贝克(Kent Beck),2008年

One way of imagining "as little as possible" is to think in terms of cost -- we're aiming for a given confidence level, so we want to achieve as much of that confidence as we can using cheap unit tests, and then make up the difference with more expensive techniques. 想象“尽可能少”的一种方法是从成本方面考虑-我们的目标是给定置信度,因此我们希望使用便宜的单元测试获得尽可能多的置信度,然后进行通过更昂贵的技术来弥补差异。

Cory Benfield's talk Building Protocol Libraries the Right Way describes an example of the kind of separation we're talking about here. 科里·本菲尔德(Cory Benfield)的演讲“正确的方法建立协议库”描述了我们在这里谈论的那种分离的例子。 The logic of how to parse an HTTP message is separable from the problem of reading the bytes. 如何解析HTTP消息的逻辑与读取字节的问题是分开的。 If you make the complicated part easy to test, and the hard to test part too simple to fail, your chances of succeeding are quite good. 如果使复杂的部分易于测试,而使难以测试的部分过于简单以至于无法失败,那么成功的机会就很大。

I think your concern is valid. 我认为您的担心是正确的。 For me, TDD is more of an evolutionary design practice than unit testing practice, but I'll save that for another discussion. 对我来说,TDD不仅仅是单元测试实践,更像是一种进化设计实践,但我将其保存起来以供讨论。

In your example, what we are really testing is that the logic contained within your individual classes is sound. 在您的示例中,我们真正测试的是您的各个类中包含的逻辑是正确的。 By stubbing the data coming from the database you have a controlled scenario that you can ensure your code works for that particular scenario. 通过对来自数据库的数据进行存根处理,您可以控制场景,从而可以确保代码可用于该特定场景。 This makes it much easier to ensure full test coverage for all data scenarios. 这使确保所有数据方案的完整测试覆盖范围变得更加容易。 You're correct that this really doesn't test the whole system end to end, but the point is to reduce the overall test maintenance costs and enable faster feedback. 您是正确的,这确实不能测试整个系统的端到端,但重点是要降低总体测试维护成本并实现更快的反馈。

My approach is to mock most collaborators at the unit test level, then write acceptance tests at the integration test level, which validates your system using real data. 我的方法是在单元测试级别上模拟大多数协作者,然后在集成测试级别上编写验收测试,从而使用真实数据验证您的系统。 Because the unit tests with their mocked data allows you to test various data scenarios out, you only need to test a few of those scenarios using integration tests to feel confident that your code will perform as you expect. 因为单元测试及其模拟数据允许您测试各种数据场景,所以您只需要使用集成测试来测试其中一些场景,以使您确信代码将按预期执行。

You can test your code against actual database in isolation. 您可以独立于实际数据库测试代码。 Just create new database instance for every test, or execute tests synchronously one after another and clean database before next test. 只需为每个测试创建新的数据库实例,或者一个接一个地同步执行测试,然后在下一个测试之前清理数据库。

But using actual database will make your tests slow, which will slow down your work, because you want quick feedback on what you are doing. 但是使用实际数据库会使测试变慢,这会减慢您的工作,因为您需要快速反馈您所做的事情。

Do not test every class - test main feature logic, which can use many different classes and mock/stub only dependencies which makes tests slow. 不要测试每个类-测试主要功能逻辑,它可以使用许多不同的类,并且只能模拟/存根依赖项,这会使测试变慢。

Find your application boundaries and tests logic between them without mocking. 找到您的应用程序边界并测试它们之间的逻辑而无需模拟。
For example in trivial web api application boundaries can be: 例如,在简单的Web api应用程序中,边界可以是:
- controller action -> request(input) -控制器动作->请求(输入)
- controller action -> response(output) -控制器动作->响应(输出)
- database -> side effect of received request. -数据库->收到请求的副作用。

Assume we live in perfect world where new database and web server setup will takes milliseconds. 假设我们生活在一个完美的世界中,新数据库和Web服务器设置将花费毫秒。 Then you will tests whole pipeline of your application: 然后,您将测试应用程序的整个管道:
1. Configure database for test 1.配置数据库进行测试
2. Send request to the web api server 2.将请求发送到Web api服务器
3. Assert that response contains expected data 3.断言回应中包含预期数据
4. Assert that database state changed as expected 4.断言数据库状态已按预期更改

But in now days world your boundaries will be controller action and abstracted database access point. 但是在当今世界,您的界限将是控制器操作和抽象的数据库访问点。 Which makes your test look like below: 这使您的测试如下所示:
1. Configure mocked database access point(repository) 1.配置模拟数据库访问点(存储库)
2. Call controller action with given parameters 2.使用给定参数调用控制器操作
3. Assert that action returns expected result 3.断言行动会返回预期结果
4. Possibly assert that mocked repository received expected update arguments. 4.可能断言模拟存储库已收到预期的更新参数。

If your application have no logic, just read/update data from database - test with actual database or, if your database framework allows it, use database in-memory. 如果您的应用程序没有逻辑,则只需从数据库中读取/更新数据-使用实际数据库进行测试,或者,如果您的数据库框架允许,请使用内存中的数据库。

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

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