【发布时间】:2016-01-07 14:14:08
【问题描述】:
我正在编写测试代码来验证第 3 方 API。 API 包含所有类型的命令,但在本次讨论中,让我们看一下布尔类型的访问器,如下所示:
// The code is simplified to explain the problem.
// Assume "Enable" does complex items like talking
// to a database, or complex calculations.
Blah item = new Blah();
item.GroupA.Enable = true;
item.GroupB.Enable = true;
// etc, say we have A LOT of "groups".
我当前的单元测试看起来像这样:
public void GroupAEnable()
{
bool val = true;
mItem.GroupA.Enable = val;
bool ret = mItem.GroupA.Enable;
VerifyEqual(val, ret);
val = false;
item.GroupA.Enable = val;
ret = mItem.GroupA.Enable;
VerifyEqual(val, ret);
}
public void GroupBEnable()
{
bool val = true;
mItem.GroupB.Enable = val;
bool ret = mItem.GroupB.Enable;
VerifyEqual(val, ret);
val = false;
mItem.GroupB.Enable = val;
ret = mItem.GroupB.Enable;
VerifyEqual(val, ret);
}
我必须为数百个“组”重复此操作。
在 C++ 中,我们可能会对其进行宏化处理并执行以下操作:
#define TEST_BOOL(cmd) \
bool val = true; \
mItem.##cmd## = val; \
bool ret = mItem.##cmd##; \
VerifyEqual(val, ret); \
val = false; \
mItem.##cmd## = val; \
ret = mItem.##cmd##; \
VerifyEqual(val, ret)
public void GroupAEnable()
{
TEST_BOOL(GroupA.Enable);
}
public void GroupBEnable()
{
TEST_BOOL(GroupB.Enable);
}
一个 C# 解决方案是创建一个接受 Action 的 TestBool 函数,但这仍然需要为每个组进行大量输入。
public void TestBool(Action setter, Action getter)
{
...
}
public void GroupAEnable()
{
TestBool(x => mItem.GroupA.Enable = x,
() => mItem.GroupA.Enable);
}
有什么很酷的方法可以简化这个吗?唯一不同的是访问器名称和类型,而测试(即测试方法)是相同的。
【问题讨论】:
-
我猜在处理糟糕的设计时,这是必要的邪恶。如果它真的只是改变的属性的名称,为什么假设行为不同?你也可以生成代码。
-
C# 不是 C++。事实上,宏也不是非常 C++ —— 它们是严重不赞成的,因为它们是 C 的遗留物。如果您不想重复代码,请将其提取到方法中。您所描述的是动态调用,在 C# 中使用
dynamic关键字已经支持 -
另一个 C#/C++ 选项 - 使用表达式或 lambdas(两种语言都支持它们)。这就是大多数测试框架中的断言的工作方式
-
@Ryuu 在 C++ 中的宏真的非常不受欢迎。这种特定的用法会导致一些心脏病发作。您正在尝试做的是两种语言的不良做法。只需使用您要进行的调用创建一个 Func 数组,然后循环调用每个调用。
-
@PanagiotisKanavos 是的,宏在 C# 中不受欢迎,所以我正在寻找替代方案。通过 lambda,它和我最底层的代码 sn-p 一样吗?无论如何,“动态”对我来说是新的......我会检查一下。
标签: c# unit-testing function-pointers