单元测试采用

发布于 2024-07-23 05:29:20 字数 1431 浏览 6 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

木槿暧夏七纪年 2024-07-30 05:29:20

提示:

避免编写过程代码

如果测试是针对严重依赖全局状态或位于丑陋方法主体深处的过程式代码编写的,则测试可能会难以维护。
如果您使用 OO 语言编写代码,请使用 OO有效地构建来减少这种情况。

  • 如果可能的话,避免全局状态。
  • 避免静态,因为它们往往会影响您的代码库,并最终导致不应该静态的事物。 它们还会使您的测试上下文变得臃肿(见下文)。
  • 有效利用多态性来防止过多的 if 和flags

查找更改的内容,将其封装并将其与保持不变的内容分开。

代码中的瓶颈点比其他部分更改得更频繁。 在您的代码库中执行此操作,您的测试将变得更加健康。

  • 良好的封装带来良好的、松散耦合的设计。
  • 重构和模块化。
  • 保持测试小而集中。

测试周围的环境越大,维护就越困难。

尽一切努力缩小测试及其执行的周围环境。

  • 使用组合方法重构来测试较小的代码块。
  • 您是否使用 TestNG 或 JUnit4 等较新的测试框架?
    它们允许您通过为测试生命周期提供更细粒度的挂钩来消除测试中的重复。
  • 研究使用测试替身(模拟、假货、存根)来减少测试上下文的大小。
  • 研究测试数据生成器模式。

消除测试中的重复,但确保它们保留焦点。

您可能无法删除所有重复项,但仍然尝试删除造成痛苦的地方。 确保不要删除太多重复项,以免别人无法一眼就看出测试的作用。 (请参阅 Paul Wheaton 的“邪恶单元测试” 文章,了解相同内容的替代解释概念。)

  • 如果无法弄清楚测试在做什么,那么没有人会愿意修复测试。
  • 遵循安排、行动、断言模式。
  • 每次测试仅使用一个断言。

在正确的级别上测试您要验证的内容。

考虑一下记录和回放 Selenium 测试所涉及的复杂性,以及与测试单一方法相比,您可能会发生什么变化。

  • 隔离彼此的依赖关系。
  • 使用依赖注入/控制反转。
  • 使用测试双打来初始化用于测试的对象,并确保您正在隔离地测试单个代码单元。
  • 确保您正在编写相关的测试
    • 通过故意引入错误来“引发陷阱”,并确保它被测试捕获。
  • 另请参阅:集成测试是骗局

了解何时使用基于状态与基于交互测试

真正的单元测试需要真正的隔离。 单元测试不会访问数据库或打开套接字。 不要再嘲笑这些互动了。 验证您与协作者的对话是否正确,而不是此方法调用的正确结果是“42”。

演示测试驱动代码

某个团队是否会测试驱动所有代码,或者为每一行代码编写“首先测试”,这是有争议的。 但他们应该至少先编写一些测试吗? 绝对地。 在某些情况下,测试优先无疑是解决问题的最佳方法。

资源:

Tips:

Avoid writing procedural code

Tests can be a bear to maintain if they're written against procedural-style code that relies heavily on global state or lies deep in the body of an ugly method.
If you're writing code in an OO language, use OO constructs effectively to reduce this.

  • Avoid global state if at all possible.
  • Avoid statics as they tend to ripple through your codebase and eventually cause things to be static that shouldn't be. They also bloat your test context (see below).
  • Exploit polymorphism effectively to prevent excessive ifs and flags

Find what changes, encapsulate it and separate it from what stays the same.

There are choke points in code that change a lot more frequently than other pieces. Do this in your codebase and your tests will become more healthy.

  • Good encapsulation leads to good, loosely coupled designs.
  • Refactor and modularize.
  • Keep tests small and focused.

The larger the context surrounding a test, the more difficult it will be to maintain.

Do whatever you can to shrink tests and the surrounding context in which they are executed.

  • Use composed method refactoring to test smaller chunks of code.
  • Are you using a newer testing framework like TestNG or JUnit4?
    They allow you to remove duplication in tests by providing you with more fine-grained hooks into the test lifecycle.
  • Investigate using test doubles (mocks, fakes, stubs) to reduce the size of the test context.
  • Investigate the Test Data Builder pattern.

Remove duplication from tests, but make sure they retain focus.

You probably won't be able to remove all duplication, but still try to remove it where it's causing pain. Make sure you don't remove so much duplication that someone can't come in and tell what the test does at a glance. (See Paul Wheaton's "Evil Unit Tests" article for an alternative explanation of the same concept.)

  • No one will want to fix a test if they can't figure out what it's doing.
  • Follow the Arrange, Act, Assert Pattern.
  • Use only one assertion per test.

Test at the right level to what you're trying to verify.

Think about the complexity involved in a record-and-playback Selenium test and what could change under you versus testing a single method.

  • Isolate dependencies from one another.
  • Use dependency injection/inversion of control.
  • Use test doubles to initialize an object for testing, and make sure you're testing single units of code in isolation.
  • Make sure you're writing relevant tests
    • "Spring the Trap" by introducing a bug on purpose and make sure it gets caught by a test.
  • See also: Integration Tests Are A Scam

Know when to use State Based vs Interaction Based Testing

True unit tests need true isolation. Unit tests don't hit a database or open sockets. Stop at mocking these interactions. Verify you talk to your collaborators correctly, not that the proper result from this method call was "42".

Demonstrate Test-Driving Code

It's up for debate whether or not a given team will take to test-driving all code, or writing "tests first" for every line of code. But should they write at least some tests first? Absolutely. There are scenarios in which test-first is undoubtedly the best way to approach a problem.

Resources:

滿滿的愛 2024-07-30 05:29:20

您是否正在测试足够小的代码单元? 除非您从根本上更改核心代码中的所有内容,否则您不应该看到太多更改。

一旦事情稳定,您将更加欣赏单元测试,但即使现在您的测试也强调了框架更改传播的程度。

这是值得的,尽你所能坚持下去。

Are you testing small enough units of code? You shouldn't see too many changes unless you are fundamentally changing everything in your core code.

Once things are stable, you will appreciate the unit tests more, but even now your tests are highlighting the extent to which changes to your framework are propogated through.

It is worth it, stick with it as best you can.

巨坚强 2024-07-30 05:29:20

如果没有更多信息,就很难准确地解释为什么您会遇到这些问题。 有时改变界面等不可避免地会破坏很多东西,有时则归结为设计问题。

尝试对您看到的失败进行分类是个好主意。 您遇到了哪些问题? 例如,它是由于 API 更改而导致的测试维护(例如在重构后进行编译!),还是由于 API 更改的行为? 如果您可以看到模式,那么您可以尝试更改生产代码的设计,或者更好地使测试免受更改。

如果更改一些内容会在许多地方对您的测试套件造成无法估量的破坏,那么您可以采取一些措施(其中大多数只是常见的单元测试技巧):

  • 开发小型代码单元并进行测试
    小的代码单元。 提炼
    接口或基类
    有意义,因此代码单元
    其中有“接缝”。 越多
    您必须引入的依赖项(或
    更糟糕的是,在类内实例化
    使用“新”),接触的越多
    改变你的代码就会。 如果每个
    代码单元有几个
    依赖关系(有时是几个或
    根本没有)那就更好了
    免受变化的影响。

  • 仅断言测试内容
    需要。 不要在中间断言,
    偶然或不相关的状态。 由...设计
    合同和合同测试(例如
    如果您正在测试堆栈弹出方法,
    之后不要测试 count 属性
    推动——这应该是在
    单独测试)。

    我看到这个问题
    相当多,特别是如果每​​次测试
    是一个变体。 如果有任何一个
    偶然的状态变化,它会崩溃
    断言其上的一切
    (是否需要断言或
    不)。

  • 就像普通代码一样,使用工厂和构建器
    在你的单元测试中。 我在大约 40 次测试后才了解到这一点
    需要在 API 更改后更新构造函数调用...

  • 同样重要的是,使用前面
    先门。 你的测试应该始终
    如果可用,请使用正常状态。 仅在必要时才使用基于交互的测试(即没有状态可验证)。

无论如何,其要点是我会尝试找出测试失败的原因/位置,然后从那里开始。 尽最大努力使自己免受变化的影响。

Without more information it's hard to make a decent stab at why you're suffering these problems. Sometimes it's inevitable that changing interfaces etc. will break a lot of things, other times it's down to design problems.

It's a good idea to try and categorise the failures you're seeing. What sort of problems are you having? E.g. is it test maintenance (as in making them compile after refactoring!) due to API changes, or is it down to the behaviour of the API changing? If you can see a pattern, then you can try to change the design of the production code, or better insulate the tests from changing.

If changing a handful of things causes untold devastation to your test suite in many places, there are a few things you can do (most of these are just common unit testing tips):

  • Develop small units of code and test
    small units of code. Extract
    interfaces or base classes where it
    makes sense so that units of code
    have 'seams' in them. The more
    dependencies you have to pull in (or
    worse, instantiate inside the class
    using 'new'), the more exposed to
    change your code will be. If each
    unit of code has a handful of
    dependencies (sometimes a couple or
    none at all) then it is better
    insulated from change.

  • Only ever assert on what the test
    needs. Don't assert on intermediate,
    incidental or unrelated state. Design by
    contract and test by contract (e.g.
    if you're testing a stack pop method,
    don't test the count property after
    pushing -- that should be in a
    separate test).

    I see this problem
    quite a bit, especially if each test
    is a variant. If any of that
    incidental state changes, it breaks
    everything that asserts on it
    (whether the asserts are needed or
    not).

  • Just as with normal code, use factories and builders
    in your unit tests. I learned that one when about 40 tests
    needed a constructor call updated after an API change...

  • Just as importantly, use the front
    door first. Your tests should always
    use normal state if it's available. Only used interaction based testing when you have to (i.e. no state to verify against).

Anyway the gist of this is that I'd try to find out why/where the tests are breaking and go from there. Do your best to insulate yourself from change.

守不住的情 2024-07-30 05:29:20

单元测试的好处之一是,当您进行这样的更改时,您可以证明您没有破坏代码。 您确实必须使测试与框架保持同步,但是这项相当平凡的工作比试图找出重构时出现的问题要容易得多。

One of the benefits of unit testing is that when you make changes like this you can prove that you're not breaking your code. You do have to keep your tests in sync with your framework, but this rather mundane work is a lot easier than trying to figure out what broke when you refactored.

如歌彻婉言 2024-07-30 05:29:20

我坚持要求您坚持使用 TDD。尝试检查您的单元测试框架,与您的团队一起进行 RCA(根本原因分析)并确定区域。

在套件级别修复单元测试代码,不要频繁更改代码,特别是函数名称或其他模块。

如果您能很好地分享您的案例研究,那么我们可以在问题领域挖掘更多内容,我们将不胜感激?

I would insists you to stick with the TDD. Try to check your Unit Testing framework do one RCA (Root Cause Analysis) with your team and identify the area.

Fix the unit testing code at suite level and do not change your code frequently specially the function names or other modules.

Would appreciate if you can share your case study well, then we can dig out more at the problem area?

残花月 2024-07-30 05:29:20

好问题!

设计良好的单元测试与设计软件本身一样困难。 开发人员很少承认这一点,因此结果往往是仓促编写的单元测试,每当被测系统发生变化时就需要维护。 因此,解决问题的部分方法可能是花费更多时间来改进单元测试的设计。

我可以推荐一本值得称道的好书单元测试的设计模式

HTH

Good question!

Designing good unit tests is hard as designing the software itself. This is rarely acknowledged by developers, so the result is often hastily-written unit tests that require maintenance whenever the system under test changes. So, part of the solution to your problem could be spending more time to improve the design of your unit tests.

I can recommend one great book that deserves its billing as The Design Patterns of Unit-Testing

HTH

请止步禁区 2024-07-30 05:29:20

如果问题是您的测试与实际代码已经过时,您可以执行以下一项或两项操作:

  1. 培训所有开发人员不要通过不更新单元测试的代码审查。
  2. 设置一个自动测试框,在每次签入后运行全套单元测试,并向破坏构建的人员发送电子邮件。 (我们曾经认为这只是针对“大男孩”的,但我们在专用盒子上使用了开源包。)

If the problem is that your tests are getting out of date with the actual code, you could do one or both of:

  1. Train all developers to not pass code reviews that don't update unit tests.
  2. Set up an automatic test box that runs the full set of units tests after every check-in and emails those who break the build. (We used to think that that was just for the "big boys" but we used an open source package on a dedicated box.)
梦里兽 2024-07-30 05:29:20

好吧,如果代码中的逻辑发生了变化,并且您已经为这些代码编写了测试,我认为需要更改测试以检查新的逻辑。 单元测试应该是相当简单的代码,用于测试代码的逻辑。

Well if the logic has changed in the code, and you have written tests for those pieces of code, I would assume the tests would need to be changed to check the new logic. Unit tests are supposed to be fairly simple code that tests the logic of your code.

木森分化 2024-07-30 05:29:20

您的单元测试正在做它们应该做的事情。 将由于框架、直接代码或其他外部源的更改而导致的任何行为中断暴露出来。 这样做的目的是帮助您确定行为是否确实发生了变化并且需要相应地修改单元测试,或者是否引入了错误从而导致单元测试失败并需要进行纠正。

不要放弃,虽然现在很沮丧,但好处将会实现。

Your unit tests are doing what they are supposed to do. Bring to the surface any breaks in behavior due to changes in the framework, immediate code or other external sources. What this is supposed to do is help you determine if the behavior did change and the unit tests need to be modified accordingly, or if a bug was introduced thus causing the unit test to fail and needs to be corrected.

Don't give up, while its frustrating now, the benefit will be realized.

迷爱 2024-07-30 05:29:20

我不确定导致难以维护代码测试的具体问题,但是当我遇到类似的测试中断问题时,我可以分享一些我自己的经验。 我最终了解到,缺乏可测试性很大程度上是由于被测类的一些设计问题:

  • 使用具体类而不是接口
  • 使用单例
  • 调用大量静态方法来进行业务逻辑和数据访问而不是接口方法

因此,我发现通常我的测试会被破坏 - 不是因为被测试的类发生了变化 - 而是因为被测试的类调用的其他类发生了变化。 一般来说,重构类以询问其数据依赖性并使用模拟对象(EasyMock 等用于 Java)进行测试使测试更加集中和可维护。 我非常喜欢一些网站,特别是与此主题相关的网站:

I'm not sure about the specific issues that make it difficult to maintain tests for your code, but I can share some of my own experiences when I had similar issues with my tests breaking. I ultimately learned that the lack of testability was largely due to some design issues with the class under test:

  • Using concrete classes instead of interfaces
  • Using singletons
  • Calling lots of static methods for business logic and data access instead of interface methods

Because of this, I found that usually my tests were breaking - not because of a change in the class under test - but due to changes in other classes that the class under test was calling. In general, refactoring classes to ask for their data dependencies and testing with mock objects (EasyMock et al for Java) makes the testing much more focused and maintainable. I've really enjoyed some sites in particular on this topic:

许一世地老天荒 2024-07-30 05:29:20

为什么每次更改框架时都必须更改单元测试? 这不应该是相反的吗?

如果您使用 TDD,那么您应该首先确定您的测试正在测试错误的行为,并且它们应该验证所需的行为是否存在。 现在您已经修复了测试,但测试失败了,您必须消除框架中的错误,直到测试再次通过。

Why should you have to change your unit tests every time you make changes to your framework? Shouldn't this be the other way around?

If you're using TDD, then you should first decide that your tests are testing the wrong behavior, and that they should instead verify that the desired behavior exists. Now that you've fixed your tests, your tests fail, and you have to go squish the bugs in your framework until your tests pass again.

悲喜皆因你 2024-07-30 05:29:20

当然,一切都是有代价的。 在开发的早期阶段,必须更改许多单元测试是很正常的。

您可能想要检查代码的某些部分以进行更多封装,创建更少的依赖项等。

当您接近生产日期时,您会很高兴您进行了这些测试,相信我:)

Everything comes with price of course. At this early stage of development it's normal that a lot of unit tests have to be changed.

You might want to review some bits of your code to do more encapsulation, create less dependencies etc.

When you near production date, you'll be happy you have those tests, trust me :)

他是夢罘是命 2024-07-30 05:29:20

您的单元测试是否过于面向黑盒? 我的意思是......让我举个例子:假设您正在对某种容器进行单元测试,您是否使用容器的 get() 方法来验证新项目是否实际存储,或者您是否设法获取一个句柄直接在存储的位置检索项目的实际存储? 后者使测试变得脆弱:当您更改实现时,您就破坏了测试。

您应该针对接口进行测试,而不是内部实现。

当您更改框架时,最好先尝试更改测试,然后再更改框架。

Aren't your unit tests too black-box oriented ? I mean ... let me take an example : suppose you are unit testing some sort of container, do you use the get() method of the container to verify a new item was actually stored, or do you manage to get an handle to the actual storage to retrieve the item directly where it is stored ? The later makes brittle tests : when you change the implementation, you're breaking the tests.

You should test against the interfaces, not the internal implementation.

And when you change the framework you'd better off trying to change the tests first, and then the framework.

输什么也不输骨气 2024-07-30 05:29:20

我建议投资一个测试自动化工具。 如果您使用持续集成,则可以使其协同工作。 有一些工具可以扫描您的代码库并为您生成测试。 然后将运行它们。 这种方法的缺点是它太通用了。 因为很多情况下单元测试的目的就是破坏系统。
我已经编写了许多测试,是的,如果代码库发生变化,我必须更改它们。

自动化工具有一个很好的界限,你肯定会有更好的代码覆盖率。

然而,通过良好的基于​​开发人员的测试,您也将测试系统的完整性。

希望这可以帮助。

I would suggest investing into a test automation tool. If you are using continuous integration you can make it work in tandem. There are tools aout there which will scan your codebase and will generate tests for you. Then will run them. Downside of this approach is that its too generic. Because in many cases unit test's purpose is to break the system.
I have written numerous tests and yes I have to change them if the codebase changes.

There is a fine line with automation tool you would definatelly have better code coverage.

However, with a well wrttien develper based tests you will test system integrity as well.

Hope this helps.

Bonjour°[大白 2024-07-30 05:29:20

如果您的代码确实很难测试并且测试代码中断或需要付出很多努力才能保持同步,那么您就会遇到更大的问题。

考虑使用提取方法重构来抽出只做一件事且只做一件事的小代码块; 无需依赖项并将测试写入这些小方法。

If your code is really hard to test and the test code breaks or requires much effort to keep in sync, then you have a bigger problem.

Consider using the extract-method refactoring to yank out small blocks of code that do one thing and only one thing; without dependencies and write your tests to those small methods.

﹏雨一样淡蓝的深情 2024-07-30 05:29:20

额外的代码似乎已经成为令人头痛的维护问题,因为当我们的内部框架发生变化时,我们必须四处寻找并修复任何与其相关的单元测试。

另一种方法是,当您的框架发生更改时,您不会测试这些更改。 或者您根本不测试框架。 那是你要的吗?

您可以尝试重构您的框架,以便它由可以独立测试的较小部分组成。 然后,当您的框架发生变化时,您希望(a)更少的部分发生变化,或者(b)变化主要在于组成部分的方式。 无论哪种方式都会让您更好地重用代码和测试。 但这涉及真正的智力努力; 不要指望这很容易。

The extra code seems to have become a maintenance headache as when our internal Framework changes we have to go around and fix any unit tests that hang off it.

The alternative is that when your Framework changes, you don't test the changes. Or you don't test the Framework at all. Is that what you want?

You may try refactoring your Framework so that it is composed from smaller pieces that can be tested independently. Then when your Framework changes, you hope that either (a) fewer pieces change or (b) the changes are mostly in the ways in which the pieces are composed. Either way will get you better reuse of both code and tests. But real intellectual effort is involved; don't expect it to be easy.

吃素的狼 2024-07-30 05:29:20

我发现,除非您使用鼓励编写非常小的类的 IoC / DI 方法,并严格遵循单一职责原则,否则单元测试最终会测试多个类的交互,这使得它们非常复杂,因此很脆弱。

我的观点是,许多新颖的软件开发技术只有一起使用才能发挥作用。 特别是 MVC、ORM、IoC、单元测试和 Mocking。 DDD(现代原始意义上的)和TDD/BDD更加独立,所以你可以使用也可以不使用它们。

I found that unless you use IoC / DI methodology that encourages writing very small classes, and follow Single Responsibility Principle religiously, the unit-tests end up testing interaction of multiple classes which makes them very complex and therefore fragile.

My point is, many of the novel software development techniques only work when used together. Particularly MVC, ORM, IoC, unit-testing and Mocking. The DDD (in the modern primitive sense) and TDD/BDD are more independent so you may use them or not.

七堇年 2024-07-30 05:29:20

有时,设计 TDD 测试会引发对应用程序本身设计的质疑。 检查您的类是否设计良好,您的方法一次只执行一件事...有了良好的设计,编写代码来测试简单的方法和类应该很简单。

Sometime designing the TDD tests launch questioning on the design of the application itself. Check if your classes have been well designed, your methods are only performing one thing a the time ... With good design it should be simple to write code to test simple method and classes.

树深时见影 2024-07-30 05:29:20

我自己也一直在思考这个话题。 我非常认同单元测试的价值,但不认同严格的 TDD。 在我看来,在某种程度上,您可能正在进行探索性编程,其中将事物划分为类/接口的方式需要改变。 如果您在旧类结构的单元测试中投入了大量时间,那么就会增加重构的惯性,放弃额外的代码会很痛苦,等等。

I have been thinking about this topic myself. I'm very sold on the value of unit tests, but not on strict TDD. It seems to me that, up to a certain point, you may be doing exploratory programming where the way you have things divided up into classes/interfaces is going to need to change. If you've invested a lot of time in unit tests for the old class structure, that's increased inertia against refactoring, painful to discard that additional code, etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文