简体   繁体   English

静态方法和单元测试

[英]static methods and unit tests

I've been reading that static methods tend to be avoided when using TDD because they tend to be hard to mock. 我一直在读,使用TDD时往往会避免使用静态方法,因为它们往往难以模拟。 I find though, that the easiest thing to unit test is a static method that has simple functionality. 我发现,单元测试最简单的方法是一个具有简单功能的静态方法。 Don't have to instantiate any classes, encourages methods that a simple, do one thing, are "standalone" etc. 不必实例化任何类,鼓励简单,做一件事,“独立”等方法。

Can someone explain this discrepancy between TDD best practices and pragmatic ease? 有人可以解释TDD最佳实践和务实轻松之间的差异吗?

thanks, A 谢谢

A static method is easy to test, but something that directly calls a static method generally is not easy to test independent of the static method it depends on. 静态方法很容易测试,但直接调用静态方法的东西通常不容易独立于它依赖的静态方法进行测试。 With a non-static method you can use a stub/mock/fake instance to ease testing, but if the code you're testing calls static methods it's effectively "hard-wired" to that static method. 使用非静态方法,您可以使用存根/模拟/假实例来简化测试,但如果您正在测试的代码调用静态方法,则它实际上与该静态方法“硬连线”。

The answer to the asked question is, in my opinion "Object Oriented seems to be all that TDD people think about." 问题的答案是,在我看来,“面向对象似乎是TDD人们所想的全部内容”。

Why? 为什么? I don't know. 我不知道。 Maybe they are all Java programmers who've been infected with the disease of making everything rely on six indirection layers, dependency injection and interface adapters. 也许他们都是Java程序员,他们已经感染了使一切依赖于六个间接层,依赖注入和接口适配器的疾病。

Java programmers seem to love to make everything difficult up front in order to "save time later." Java程序员似乎喜欢预先让一切变得困难,以“以后节省时间”。

I advise applying some Agile principles to your TDD: If it isn't causing a problem then don't fix it. 我建议将一些敏捷原则应用到您的TDD:如果它没有引起问题,那么不要修复它。 Don't over design. 不要过度设计。

In practice I find that if the static methods are tested well first then they are not going to be the cause of bugs in their callers. 在实践中,我发现如果首先测试静态方法,那么它们不会成为调用者中的错误的原因。

If the static methods execute quickly then they don't need a mock. 如果静态方法快速执行,那么它们不需要模拟。

If the static methods work with stuff from outside the program, then you might need a mock method. 如果静态方法使用程序外部的东西,那么您可能需要一个模拟方法。 In this case you'd need to be able to simulate many different kinds of function behavior. 在这种情况下,您需要能够模拟许多不同类型的函数行为。

If you do need to mock a static method remember that there are ways to do it outside of OO programming. 如果你确实需要模拟一个静态方法,请记住 OO编程之外有办法做到这一点。

For example, you can write scripts to process your source code into a test form that calls your mock function. 例如,您可以编写脚本来将源代码处理为调用mock函数的测试表单。 You could link different object files that have different versions of the function into the test programs. 您可以将具有不同版本功能的不同目标文件链接到测试程序中。 You could use linker tricks to override the function definition (if it didn't get inlined). 您可以使用链接器技巧来覆盖函数定义(如果它没有内联)。 I am sure there are some more tricks I haven't listed here. 我相信还有一些我没有在这里列出的技巧。

It's easy to test the static method. 测试静态方法很容易。 The problem is that there is no way to isolate your other code from that static method when testing the other code. 问题是在测试其他代码时无法将其他代码与静态方法隔离开来。 The calling code is tightly-coupled to the static code. 调用代码与静态代码紧密耦合。

A reference to a static method cannot be mocked by many mocking frameworks nor can it be overridden. 静态方法的引用不能被许多模拟框架嘲笑,也不能被覆盖。

If you have a class that is making lots of static calls, then to test it you have to configure the global state of the application for all of those static calls - so maintenance becomes a nightmare. 如果你有一个正在进行大量静态调用的类,那么要测试它,你必须为所有这些静态调用配置应用程序的全局状态 - 因此维护变成了一场噩梦。 And if your test fails, then you don't know which bit of code caused the failure. 如果您的测试失败,那么您不知道哪个位代码导致了失败。

Getting this wrong, is one of the reasons that many developers think TDD is nonsense. 弄错了,是许多开发人员认为TDD无意义的原因之一。 They put in a huge maintenance effort for test results that only vaguely indicate what went wrong. 他们为测试结果付出了巨大的维护费用,只是模糊地指出出了什么问题。 If they'd only reduced the coupling between their units of code, then maintenance would be trivial and the test results specific. 如果他们只减少了他们的代码单元之间的耦合,那么维护将是微不足道的,并且测试结果是特定的。

That advice is true for the most part.. but not always. 这个建议在大多数情况下都是正确的......但并非总是如此。 My comments are not C++ specific.. 我的评论不是C ++特有的..

  1. writing tests for static methods (which are pure/stateless functions): ie the work off the inputs to produce a consistent result. 为静态方法( 函数/无状态函数)编写测试:即输出输出以产生一致的结果。 eg Add below - will always give the same value given a particular set of inputs. 例如,在下面添加 - 在给定一组特定输入的情况下,将始终给出相同的值。 There is no problem in writing tests for these or code that calls such pure static methods. 为这些或代码调用这些纯静态方法编写测试没有问题
  2. writing tests for static methods which consume static state : eg GetAddCount() below. 为静态方法编写消耗静态的测试:例如下面的GetAddCount()。 Calling it in multiple tests can yield different values. 在多个测试中调用它可以产生不同的值。 Hence one test can potentially harm the execution of another test - tests need to be independent. 因此,一项测试可能会损害另一项测试的执行 - 测试需要独立。 So now we need to introduce a method to reset static state such that each test can start from a clean slate (eg something like ResetCount()). 所以现在我们需要引入一种方法来重置静态状态,这样每个测试都可以从一个干净的平板开始(比如像ResetCount()这样的东西)。
  3. Writing tests for code that accesses static methods but no source-code access to the dependency: Once again depends on the properties of the static methods themselves. 编写访问静态方法没有源代码访问依赖关系的代码的测试:再次依赖于静态方法本身的属性。 However if they are gnarly, you have a difficult dependency. 但是,如果它们很粗糙,那么你就会有一种困难的依赖。 If the dependency is an object, then you could add a setter to the dependent type and set/inject a fake object for your tests. 如果依赖项是一个对象,那么您可以将一个setter添加到依赖类型,并为您的测试设置/注入一个假对象。 When the dependency is static, you may need a sizable refactoring before you can get tests running reliably. 当依赖项是静态的时,您可能需要进行大规模的重构才能使测试可靠地运行。 (eg Add an object middle-man dependency that delegates to the static method. Now plugin a fake middle-man for your tests) (例如,添加一个委托给静态方法的对象中间人依赖。现在为你的测试插入一个假中间人)

Lets take an example 让我们举个例子

public class MyStaticClass
{
  static int __count = 0;
  public static int GetAddCount()
  {  return ++__count;  }

  public static int Add(int operand1, int operand2)
  {  return operand1 + operand2; }

  // needed for testability
  internal static void ResetCount()
  {
     __count = 0;
  }
}

...

//test1
MyStaticClass.Add(2,3);        // => 5
MyStaticClass.GetAddCount();    // => 1

// test2
MyStaticClass.Add(2,3);  // => 5
//MyStaticClass.ResetCount(); // needed for tests
MyStaticClass.GetAddCount();  // => unless Reset is done, it can differ from 1

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

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