TDD,如果两个 public 方法只使用一个私有方法,那么如何测试它们?
TDD, how to tests two public methods if they use only one private method on nothing else?
是否需要测试 2 个方法,如果它们只使用一个具有相似参数的私有方法?
例如我有一些接口(合同):
public interface IInterface
{
void Method1(arg1, arg2, arg3);
void Method2(arg1, arg2, arg3);
}
和此接口的实现:
public class MyClass : IInterface
{
public void Method1(arg1, arg2, arg3)
{
Method3(arg1, arg2, arg3)
}
public void Method2(arg1, arg2, arg3)
{
Method3(arg1, arg2, arg3)
}
private void Method3(arg1, arg2, arg3)
{
// handle data
}
}
例如,我有 3 个方法 1 测试,是否需要根据 TDD/RGB 规则
复制粘贴方法 2 的测试
Is it required to copy-paste this tests for Method2 based on TDD/RGB rules
Required有点太强了; TDD 警察不会来踢你的门。
但你肯定在画外线。
如果 MyClass.Method2
在所有有趣的情况下确实应该具有与 MyClass.Method1
相同的行为,那么您应该进行测试以记录此 属性.
该测试不需要是现有测试的复制粘贴;它可能类似于数据驱动测试。
[TestMethod()]
public void testMethodEquivalence () {
val arg1 = TestContext.DataRow["arg1"]
val arg2 = TestContext.DataRow["arg2"]
val arg3 = TestContext.DataRow["arg3"]
MyClass expected = new MyClass();
MyClass sut = new MyClass();
Assume.That(sut, Is.EqualTo(expected))
expected.Method1(arg1, arg2, arg3)
sut.Method2(arg1, arg2, arg3)
Assert.That(sut, Is.EqualTo(expected))
}
重点是,在某个地方,应该对 MyClass.Method2 的实施有足够的限制,如果任何更改违反 class 合同,新编码员将毫不留情地提醒重构。
单元测试的核心要点是验证 public 被测代码的合同。
从这个意义上说 - 第一个观察结果是:在您的示例中,两种方法都采用完全相同的参数并执行完全相同的操作。那么合理的答案是:您不需要 方法 1 和方法 2 - 您可以制作方法 3 public 并调用它。
假设您的示例过于简单化,您确实有两个个选择:
- 集中测试两种 public 方法
- 专注于 Method1,并对 Method2 进行一些 "ok, it works" 测试
这里没有黄金法则 - 您应该退后一步,决定您的实施方式有多大 可能 会发生变化。但在那种情况下,你会做 TDD,对吧。这意味着如果您更改 Method2,您将 首先 检查现有测试。当您 然后 发现它们还不够时 - 您可以添加更多内容。
从那里,我会选择选项 2。
所以听起来您并不想重构 class 的调用者以使用一个简化的界面。如果使用它的地方很普遍,或者在共享代码库中,你不能随意重构,那可能是合理的。
我要做的是将这个 class 变成一个外观,以获得更简洁的界面。换句话说,将您的私有方法拉入具有简洁界面的新 class。然后使用现有的 class 以几乎相同的方式使用,但增加了对真实实现的依赖性。
这有几个优点:
- 您可以开始将 class 依赖于您的原始接口的元素逐渐转移到新的实现中。 (特别是如果您弃用这些方法以提醒其他开发人员。)最终可以完全删除外观。
- 您可以对新 class.
进行广泛的单元测试
- 您可以编写两个非常简单的单元测试来验证您是否在现有 class 中调用此依赖项。 (无需再测试每个案例。)
是否需要测试 2 个方法,如果它们只使用一个具有相似参数的私有方法?
例如我有一些接口(合同):
public interface IInterface
{
void Method1(arg1, arg2, arg3);
void Method2(arg1, arg2, arg3);
}
和此接口的实现:
public class MyClass : IInterface
{
public void Method1(arg1, arg2, arg3)
{
Method3(arg1, arg2, arg3)
}
public void Method2(arg1, arg2, arg3)
{
Method3(arg1, arg2, arg3)
}
private void Method3(arg1, arg2, arg3)
{
// handle data
}
}
例如,我有 3 个方法 1 测试,是否需要根据 TDD/RGB 规则
复制粘贴方法 2 的测试Is it required to copy-paste this tests for Method2 based on TDD/RGB rules
Required有点太强了; TDD 警察不会来踢你的门。
但你肯定在画外线。
如果 MyClass.Method2
在所有有趣的情况下确实应该具有与 MyClass.Method1
相同的行为,那么您应该进行测试以记录此 属性.
该测试不需要是现有测试的复制粘贴;它可能类似于数据驱动测试。
[TestMethod()]
public void testMethodEquivalence () {
val arg1 = TestContext.DataRow["arg1"]
val arg2 = TestContext.DataRow["arg2"]
val arg3 = TestContext.DataRow["arg3"]
MyClass expected = new MyClass();
MyClass sut = new MyClass();
Assume.That(sut, Is.EqualTo(expected))
expected.Method1(arg1, arg2, arg3)
sut.Method2(arg1, arg2, arg3)
Assert.That(sut, Is.EqualTo(expected))
}
重点是,在某个地方,应该对 MyClass.Method2 的实施有足够的限制,如果任何更改违反 class 合同,新编码员将毫不留情地提醒重构。
单元测试的核心要点是验证 public 被测代码的合同。
从这个意义上说 - 第一个观察结果是:在您的示例中,两种方法都采用完全相同的参数并执行完全相同的操作。那么合理的答案是:您不需要 方法 1 和方法 2 - 您可以制作方法 3 public 并调用它。
假设您的示例过于简单化,您确实有两个个选择:
- 集中测试两种 public 方法
- 专注于 Method1,并对 Method2 进行一些 "ok, it works" 测试
这里没有黄金法则 - 您应该退后一步,决定您的实施方式有多大 可能 会发生变化。但在那种情况下,你会做 TDD,对吧。这意味着如果您更改 Method2,您将 首先 检查现有测试。当您 然后 发现它们还不够时 - 您可以添加更多内容。
从那里,我会选择选项 2。
所以听起来您并不想重构 class 的调用者以使用一个简化的界面。如果使用它的地方很普遍,或者在共享代码库中,你不能随意重构,那可能是合理的。
我要做的是将这个 class 变成一个外观,以获得更简洁的界面。换句话说,将您的私有方法拉入具有简洁界面的新 class。然后使用现有的 class 以几乎相同的方式使用,但增加了对真实实现的依赖性。
这有几个优点:
- 您可以开始将 class 依赖于您的原始接口的元素逐渐转移到新的实现中。 (特别是如果您弃用这些方法以提醒其他开发人员。)最终可以完全删除外观。
- 您可以对新 class. 进行广泛的单元测试
- 您可以编写两个非常简单的单元测试来验证您是否在现有 class 中调用此依赖项。 (无需再测试每个案例。)