【问题标题】:Unit-testing private methods: Facade pattern单元测试私有方法:外观模式
【发布时间】:2009-01-08 20:13:43
【问题描述】:

许多开发人员认为测试私有方法是个坏主意。但是,我发现的所有示例都是基于私有方法是私有的,因为调用它们可能会破坏内部对象的状态。但这不仅仅是隐藏方法的原因。

让我们考虑外观模式。我的班级用户需要 2 个公共方法。它们太大了。在我的示例中,他们需要从数据库的 BLOB 中加载一些复杂的结构,对其进行解析,填充一些临时 COM 对象,运行用户宏来验证和修改这些对象,并将修改后的对象序列化为 XML。单个方法的功能相当大:-) 这两种公共方法都需要大多数这些操作。因此,我创建了大约 10 个私有方法,并且有 2 个公共方法调用它们。实际上,我的私有方法不一定是私有的;他们不会破坏实例的内部状态。但是,当我不习惯测试私有方法时,我有以下问题:

  1. 发布它们对用户来说意味着复杂性(他们有一个不需要的选择)
  2. 我无法想象这样一个大型公共方法的 TDD 风格,当您编写 500 多行代码只是为了返回一些东西(甚至不是真正的结果)。
  3. 这些方法的数据是从数据库中检索的,测试与 DB 相关的功能要困难得多。

当我测试私有方法时:

  1. 我不会发布会让用户感到困惑的细节。公共接口包括 2 个方法。
  2. 我可以以 TDD 风格工作(逐步编写小方法)。
  3. 我可以使用测试数据覆盖大部分课程的功能,而无需连接数据库。

谁能描述一下,我做错了什么?我应该使用什么设计来获得相同的奖金并且不测试私有方法?

更新:在我看来,我已经将我所能做的一切都提取到了另一个类中。所以,我无法想象我还能提取什么。从数据库加载由 ORM 层执行,解析流,序列化为 XML,运行宏 - 一切都由独立类完成。此类包含相当复杂的数据结构、搜索和转换例程,并调用所有提到的实用程序。所以,我不认为可以提取其他东西。否则,它的职责(有关数据结构的知识)将在各个类之间进行划分。

所以,我现在看到的最好的解决方法是分成 2 个对象(外观本身和真实对象,私有方法变为公共)并将真实对象移动到没人会试图找到它的地方。在我的情况下(Delphi),它将是一个独立的单元,在其他语言中它可能是一个单独的名称空间。其他类似的选项是 2 个接口,感谢您的想法。

【问题讨论】:

  • 似乎已经有一个与此相同的问题:stackoverflow.com/questions/250692/…
  • 这是关于在 C# 中测试私有方法的博客coolaspdotnetcode.com/Web/…
  • 外观不是一个实现,因此您对外观的测试不应该测试外观运行的所有实现是否有效。如果有的话,您可以通过外观运行一些系统/集成测试,但您的单元应该单独测试。然后你可以测试这些东西是否被正确地粘合在一起(例如,可以在不爆炸的情况下运行),但是当你处理更高级别的抽象时不要担心这些小事情。

标签: unit-testing tdd


【解决方案1】:

我认为您在外观中投入了太多责任(实施)。我通常会认为这是其他类中实际实现的前端。

因此,外观中的私有方法很可能是一个或多个其他类中的公共方法。然后你可以在那里测试它们。

【讨论】:

  • 请查看更新。这个类的职责是持有相当复杂的数据结构和例程来进行搜索、转换等。其他所有东西都被提取到外部。所以,Facade 本身可以被提取,但我不认为其他任何东西也可以被提取。
【解决方案2】:

谁能描述一下,我是什么 做错了吗?

也许什么都没有?

如果我想测试一个方法,我将其设为默认(包)范围并对其进行测试。

您已经提到了另一个很好的解决方案:使用您的两种方法创建一个接口。您的客户访问这两种方法,其他方法的可见性无关紧要。

【讨论】:

    【解决方案3】:

    私有方法用于封装一些在您尝试测试的类之外没有意义的行为。您永远不必测试私有方法,因为只有同一类的公共或受保护方法才会调用私有方法。

    可能只是您的课程非常复杂,需要花费大量精力来测试它。但是,我建议您寻找可以分解为自己的类的抽象。这些类将具有较小的项目范围和要测试的复杂性。

    【讨论】:

      【解决方案4】:

      我不熟悉您的要求和设计,但您的设计似乎是程序性的,而不是面向对象的。即您有 2 个公共方法和许多私有方法。如果您将您的类分解为每个对象都有其角色的对象,那么测试每个“小”类会更容易。此外,您可以将“助手”对象访问级别设置为包(Java 中的默认值,我知道 C# 中有类似的访问级别)这样您就不会在 API 中公开它们,但您可以独立地对它们进行单元测试(如它们是单位)。

      【讨论】:

        【解决方案5】:

        如果你花时间看看 Clean Code Tech talks 来自米什科。他对如何编写代码以进行测试非常有洞察力。

        【讨论】:

          【解决方案6】:

          这是一个有点争议的话题……大多数 TDD 人员认为重构方法以简化单元测试实际上会使您的设计更好。我认为这通常是正确的,但公共 API 的私有方法的特定情况绝对是一个例外。所以,是的,你应该测试私有方法,不,你不应该公开它。

          如果您使用 Java,这是我编写的一个实用方法,可以帮助您测试类中的静态私有方法:

          import java.lang.reflect.InvocationTargetException;
          import java.lang.reflect.Method;
          import junit.framework.Assert;
          
          public static Object invokeStaticPrivateMethod(Class<?> clazz, String methodName, Object... params) {
              Assert.assertNotNull(clazz);
              Assert.assertNotNull(methodName);
              Assert.assertNotNull(params);
          
              // find requested method
              final Method methods[] = clazz.getDeclaredMethods();
              for (int i = 0; i < methods.length; ++i) {
                  if (methodName.equals(methods[i].getName())) {
                      try {
                          // this line makes testing private methods possible :)
                          methods[i].setAccessible(true);
                          return methods[i].invoke(clazz, params);
                      } catch (IllegalArgumentException ex) {
                          // maybe method is overloaded - try finding another method with the same name
                          continue;
                      } catch (IllegalAccessException ex) {
                          Assert.fail("IllegalAccessException accessing method '" + methodName + "'");
                      } catch (InvocationTargetException ex) {
                          // this makes finding out where test failed a bit easier by
                          // purging unnecessary stack trace
                          if (ex.getCause() instanceof RuntimeException) {
                              throw (RuntimeException) ex.getCause();
                          } else {
                              throw new InvocationException(ex.getCause());
                          }
                      }
                  }
              }
          
              Assert.fail("method '" + methodName + "' not found");
              return null;
          }
          

          这可能也可以为非静态方法重写,但是那些讨厌的私有方法通常是私有的,所以我从来不需要它。 :)

          【讨论】:

            【解决方案7】:

            假设您有 8 个私有方法和 2 个公共方法。如果您可以独立执行私有方法,即不调用任何其他方法,并且没有破坏状态的副作用,那么仅对该方法进行单元测试是有意义的。但是在那种情况下,方法不需要私有!

            在 C# 中,我会将此类方法设为受保护而不是私有,并将它们公开在子类中以供测试

            考虑到您的情况,将可测试方法公开并让用户拥有真正的外观可能更有意义,其中只有他们接口所需的 2 个公共方法

            【讨论】:

              猜你喜欢
              • 2014-08-10
              • 1970-01-01
              • 2013-11-09
              • 1970-01-01
              • 2015-04-09
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多