C# TDD:是否有持续测试的模式?
常量是美丽的人——它们可以在一个独特的地方保存一个在代码中随处可见的值。更改该值只需要一次简单的修改 生活很酷 这就是承诺。现实有时是不同的:C# TDD:是否有持续测试的模式?,c#,testing,tdd,constants,C#,Testing,Tdd,Constants,常量是美丽的人——它们可以在一个独特的地方保存一个在代码中随处可见的值。更改该值只需要一次简单的修改 生活很酷 这就是承诺。现实有时是不同的: 您将LogCompleteFileName常量值从L:\LOGS\MyApp.log更改为\\Traces\App208.txt,您将得到两个文件:用于跟踪的\\Traces\App208.txt,以及用于日志的\\Traces\App208.txt.log 您将TransactionTimeout从2分钟更改为4分钟,但2分钟后仍然会有一个超时(花了
- 您将
常量值从LogCompleteFileName
更改为L:\LOGS\MyApp.log
,您将得到两个文件:用于跟踪的\\Traces\App208.txt
,以及用于日志的\\Traces\App208.txt
\\Traces\App208.txt.log
- 您将
从2分钟更改为4分钟,但2分钟后仍然会有一个超时(花了一天的时间后,您发现还必须更改DBMS的超时和事务管理器的超时…)TransactionTimeout
- 将
从SleepTimeInMinutes
替换为1
,您看不到任何变化(大约一小时后,您发现常量的名称有误导性:粒度不是分钟而是毫秒…)10
- 更微妙的是:您将
从(比如CompanyName
更改为Yahoo
),但自动邮件提醒仍会发送到Microsoft
alert@yahoo.com
const
关键字
您如何使用TDD测试您的(所谓的)常量
非常感谢:)
测试常量(!)值的更改的唯一方法是将该常量设置为应用程序设置
在我看来,您在问题中列出的所有用途听起来像是应用程序设置,而不是常量。常数是一个常数,例如:
const decimal LITERS_PER_HOGSHEAD = 238.480942392;
编辑添加:希望这比我轻率的回答更有帮助。我通常创建一个AppSettings类。这个类中的一些属性是从配置文件中提取的,有些是我不希望更改的设置,有些可以是常量
public class AppSettings
{
public const decimal GILLS_PER_HOMER = 1859.771248601;
public string HelpdeskPhone
{
get { // pulled from config and cached at startup }
}
public int MaxNumberOfItemsInAComboBox
{
get { return 3; }
}
}
从我读到的你的问题来看,这与TDD无关。您描述的用法不是一个真正的常量,而是一个配置值,因此在这些情况下,您不应该使用
const
修饰符。我觉得您使用常量主要是为了说明配置设置。这是ConfigurationManager的理想选择,但也很难用于测试
我建议使用以下方法:
const String SomeValue = "TESTCONSTANT";
static class ConfigurationSettings
{
static String SomeProperty
{
get
{
var result = SomeValue;
if (ConfigurationManager.AppSettings["SOMEKEY"] != null)
result = ConfigurationManager.AppSettings["SOMEKEY"];
return result;
}
}
}
有几件事
首先,TDD和TDD促进的紧急设计是将您的职责分离,应用干式和依赖注入
对常量进行单元测试很容易,而且可能有点毫无意义
但在我看来,测试另一个单元对该常数的评估不是单元测试。这是一个集成测试。在其他单位中测试常量值将由模拟或存根覆盖
其次,你的例子多种多样:
您的日志示例仅使用1个文件。只是有两个存在。如果要求只存在一个文件,那么您可以为此制作一个测试
事务超时测试应通过集成测试进行。它应该表明您的原始测试不是问题所在
更改公司名称是可以的,因为它与公司名称有关。域名应该而且可能是不同的常量
正如其他人提到的,在测试其他类时,在我的周围传递一个配置类可能有助于mock/stub。这是因为所有这些东西都不是常量。。。实际上是:
- G(6.67300×10-11 m3 kg-1 s-2)
- c(299 792 458米/秒)
- pi(3.1415926535897931)
- L(6.0221415×1023 mol-1)
如果其中任何一种情况发生变化,请不要担心,应用程序崩溃将是最后一个重要因素xD有两种常量:
public class Longcat {
// Source: http://encyclopediadramatica.com/Longcat
public static final String HEAD_LINES = "" +
" /\\___/\\ \n" +
" / \\ \n" +
" | # # | \n" +
" \\ @ | \n" +
" \\ _|_ / \n" +
" / \\______ \n" +
" / _______ ___ \\ \n" +
" |_____ \\ \\__/ \n" +
" | \\__/ \n";
public static final String BODY_LINE = "" +
" | | \n";
public static final String FEET_LINES = "" +
" / \\ \n" +
" / ____ \\ \n" +
" | / \\ | \n" +
" | | | | \n" +
" / | | \\ \n" +
" \\__/ \\__/ \n";
...
1) 为方便/可读性而设置的常量
当使用TDD编写代码时,每行生产代码都应该存在,因为首先有一个失败的测试要求编写该代码。当您重构代码时,一些神奇的值将被提升为常量。其中一些可能还可以用作应用程序设置,但为了方便(代码更少),它们是在代码中配置的,而不是在外部配置文件中配置的
在这种情况下,按照我编写测试的方式,生产代码和测试代码都将使用相同的常量。测试将指定按预期使用常数。但是测试不会重复常量的值,例如“assert MAX_ITEMS==4
”,因为这将是重复的代码。相反,测试将检查某些行为是否正确使用常量
例如,是一个示例应用程序(由我编写),它将打印指定长度的。如您所见,Longcat被定义为一系列常量:
public class Longcat {
// Source: http://encyclopediadramatica.com/Longcat
public static final String HEAD_LINES = "" +
" /\\___/\\ \n" +
" / \\ \n" +
" | # # | \n" +
" \\ @ | \n" +
" \\ _|_ / \n" +
" / \\______ \n" +
" / _______ ___ \\ \n" +
" |_____ \\ \\__/ \n" +
" | \\__/ \n";
public static final String BODY_LINE = "" +
" | | \n";
public static final String FEET_LINES = "" +
" / \\ \n" +
" / ____ \\ \n" +
" | / \\ | \n" +
" | | | | \n" +
" / | | \\ \n" +
" \\__/ \\__/ \n";
...
这些测试验证了常数的使用是否正确,但不会重复常数的值。如果我更改常量的值,所有测试将自动使用新值。(至于Longcat ASCII艺术是否正确,则需要手动验证。尽管您甚至可以通过验收测试实现自动化,这对于更大的项目来说是值得推荐的。)
2) 普适常数
同一个应用程序也有一些常数,它们从来都不是e
public void test__Identity_conversion() {
int feet1 = 10000;
int feet2 = FEET.from(feet1, FEET);
assertEquals(feet1, feet2);
}
public void test__Convert_feet_to_meters() {
int feet = 10000;
int meters = METERS.from(feet, FEET);
assertEquals(3048, meters);
}
public void test__Convert_meters_to_feet() {
int meters = 3048;
int feet = FEET.from(meters, METERS);
assertEquals(10000, feet);
}
public enum LengthUnit {
METERS("m", 1.0), FEET("ft", 0.3048), PETRONAS("petronas", 451.9), LINES("lines", 0.009);
private final String name;
private final double lengthInMeters;
...
public int from(int length, LengthUnit unit) {
return (int) (length * unit.lengthInMeters / this.lengthInMeters);
}
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod]
public void DefaultValue_Equals_8()
{
Assert.AreEqual<int>(8, MyNamespace.MyClass.DefaultValue);
}
namespace MyNamespace
{
public class MyClass
{
public const int DefaultValue = 8;
}
}
Assert.AreEqual<decimal>(29.5276, converter.metresToFeet(9))