每个开发人员都希望促进代码重用。 通常,重用是一件好事。 它使代码更易于维护且更正确。
但是,有一个地方不应发生重用,而这又跨越了生产和测试代码之间的界限。 常量就是一个很好的例子。 让我举一个具体的例子来解释我的意思。
假设您的应用程序中有此FizzBuzz代码,FizzBuzz.java文件:
String[] generate(int start, int end) {
String[] result = new String[end - start + 1];
for (int x = 0, number = start; number <= end; x++, number++) {
if (number % 15 == 0) {
result[x] = "FizzBuzz";
} else if (number % 3 == 0) {
result[x] = "Fizz";
} else if (number % 5 == 0) {
result[x] = "Buzz";
} else {
result[x] = String.valueOf(number);
}
}
return result;
}
然后,您在其中编写了一些基本的单元测试,例如FizzBuzzTest.java文件:
@Test
void fizzTest() {
FizzBuzz fizzBuzz = new FizzBuzz();
String[] result = fizzBuzz.generate(3, 3);
assertEquals("Fizz", result[0]);
}
@Test
void buzzTest() {
FizzBuzz fizzBuzz = new FizzBuzz();
String[] result = fizzBuzz.generate(5, 5);
assertEquals("Buzz", result[0]);
}
@Test
void fizzBuzzTest() {
FizzBuzz fizzBuzz = new FizzBuzz();
String[] result = fizzBuzz.generate(15, 15);
assertEquals("FizzBuzz", result[0]);
}
一些开发人员很想将解决方案重构为常量FizzBuzz.java然后它也会显示在测试中:
public class FizzBuzz {
public static final String FIZZ = "Fizz";
public static final String BUZZ = "Buzz";
String[] generate(int start, int end) {
String[] result = new String[end - start + 1];
for (int x = 0, number = start; number <= end; x++, number++) {
if (number % 15 == 0) {
result[x] = FIZZ + BUZZ;
} else if (number % 3 == 0) {
result[x] = FIZZ;
} else if (number % 5 == 0) {
result[x] = BUZZ;
} else {
result[x] = String.valueOf(number);
}
}
return result;
}
而在FizzBuzzTest.java:
@Test
void fizzTest() {
FizzBuzz fizzBuzz = new FizzBuzz();
String[] result = fizzBuzz.generate(3, 3);
assertEquals(FIZZ, result[0]);
}
@Test
void buzzTest() {
FizzBuzz fizzBuzz = new FizzBuzz();
String[] result = fizzBuzz.generate(5, 5);
assertEquals(BUZZ, result[0]);
}
@Test
void fizzBuzzTest() {
FizzBuzz fizzBuzz = new FizzBuzz();
String[] result = fizzBuzz.generate(15, 15);
assertEquals(FIZZ + BUZZ, result[0]);
}
Reasons not to do this
为什么不应该这样做有两个关键原因。
Tests change when production code changes
不这样做的一个原因是,这会导致一种情况,当重构生产代码时,不必要地将测试重构。 考虑一下如果您进行IDE重构以重命名常量会发生什么情况模糊或将其移至另一堂课。 您还将看到重构会自动触摸测试代码。 这可能令人不安,因为它对测试是否仍然准确提出了疑问。
理想的测试代码是在重构生产代码时不会更改的代码,并且测试不断通过。 这告诉您您的重构是100%安全的。 通常,您不必更改测试代码即可更改生产代码,如果这样做,则应该是因为功能其实是变化。 不幸的是,通过引用生产中的常量的测试,由于将它们耦合在一起,因此无法获得保证。
Tests will pass when the code is wrong
更重要的是,引用生产常量的测试可能会导致以下情况:代码实际上是错误的,但是单元测试还是通过了。
考虑“ Fizz”拼写错误或什至值错误的情况,因为这里的值嗡嗡声:
public class FizzBuzz {
public static final String FIZZ = "Buzz";
// . . .
单元测试仍将通过,因为测试针对的是相同的错误嘶嘶声生产代码引用的变量。
在实际的生产代码中,这确实是可怕的和致命的杀伤力,其中常量的值通常与系统进行的某些外部交互有关。 例如,Web服务调用上的查询参数或从前端收到的表单字段的名称。
Unit testing should always be the software equivalent to double-entry bookkeeping. This means that unit tests are meant to repeat values as a way to provide a second point-of-view on the correctness of a given piece of code. The unit test should check for the literal String value "Fizz"
even if the production code uses a constant. This guarantees that the literal value expected from the constant is maintained.
Conclusion
由于这些原因,我得出结论,测试代码不应在生产代码中引用常量。 希望能帮助到你。