编写/实现 API:可测试性与信息隐藏

2023-12-02

很多时候,我在参与 API 的设计/实现时都面临着这样的困境。

我是一个非常坚定的支持者信息隐藏并尝试为此使用各种技术,包括但不限于内部类、私有方法、包私有限定符等。

这些技术的问题在于它们往往会妨碍良好的可测试性。虽然其中一些技术可以解决(例如,通过将类放入同一个包中来实现包私有性),但其他技术则无法解决没那么容易对付要么需要反射魔法或其他技巧。

我们看一下具体的例子:

public class Foo {
   SomeType attr1;
   SomeType attr2;
   SomeType attr3;

   public void someMethod() {
      // calculate x, y and z
      SomethingThatExpectsMyInterface something = ...;
      something.submit(new InnerFoo(x, y, z));
   }

   private class InnerFoo implements MyInterface {
      private final SomeType arg1;
      private final SomeType arg2;
      private final SomeType arg3;

      InnerFoo(SomeType arg1, SomeType arg2, SomeType arg3) {
         this.arg1 = arg1;
         this.arg2 = arg2;
         this.arg3 = arg3;
      }

      @Override
      private void methodOfMyInterface() {
         //has access to attr1, attr2, attr3, arg1, arg2, arg3
      }
   }
}

有充分的理由不暴露InnerFoo- 任何其他类、库都不应访问它,因为它没有定义任何公共契约,并且作者故意不希望它可访问。然而,为了使其 100% TDD-kosher 并且无需任何反射技巧即可访问,InnerFoo应该这样重构:

private class OuterFoo implements MyInterface {
   private final SomeType arg1;
   private final SomeType arg2;
   private final SomeType arg3;
   private final SomeType attr1;
   private final SomeType attr2;
   private final SomeType attr3;

   OuterFoo(SomeType arg1, SomeType arg2, SomeType arg3, SomeType attr1, SomeType attr2, SomeType attr3) {
      this.arg1 = arg1;
      this.arg2 = arg2;
      this.arg3 = arg3;
      this.attr1 = attr1;
      this.attr2 = attr2;
      this.attr3 = attr3;
   }

   @Override
   private void methodOfMyInterface() {
      //can be unit tested without reflection magic
   }
}

这个例子只涉及 3 个属性,但是有 5-6 个属性是相当合理的,OuterFoo构造函数必须接受 8-10 个参数!在顶部添加 getters,你已经有 100 行完全无用的代码(还需要 getters 来获取这些属性进行测试)。是的,我可以通过提供构建器模式让情况好一点,但我认为这不仅是过度设计,而且违背了 TDD 本身的目的!

此问题的另一个解决方案是公开类的受保护方法Foo,将其扩展为FooTest并获取所需数据。再说一次,我认为这也是一个不好的方法,因为protected method 是否定义了合同通过公开它,我现在已经隐含地签署了它。

别误会我的意思。我喜欢编写可测试的代码. 我喜欢简洁、干净的 API、短代码块、可读性等等。但我不喜欢的是在信息隐藏方面做出任何牺牲只是因为单元测试更容易.

任何人都可以对此提供任何想法(一般而言,特别是)?对于给定的示例,还有其他更好的解决方案吗?


对于此类事情,我的首选答案是“测试代理”。在您的测试包中,从被测系统派生一个子类,其中包含受保护数据的“直通”访问器。

优点:

  • 您可以直接测试或模拟您不想公开的方法。
  • 由于测试代理位于测试包中,因此您可以确保它永远不会在生产代码中使用。
  • 与直接测试类相比,测试代理需要对代码进行更少的更改才能使其可测试。

缺点:

  • 该类必须是可继承的(不final)
  • 您需要访问的任何隐藏成员都不能是私有的;受保护是你能做的最好的事情。
  • 这并不是严格意义上的 TDD;而是 TDD。 TDD 适合一开始就不需要测试代理的模式。
  • 严格来说,这甚至不是单元测试,因为在某种程度上,您依赖于代理和实际 SUT 之间的“集成”。

简而言之,这通常应该是罕见的。我倾向于仅将它用于 UI 元素,其中最佳实践(以及许多 IDE 的默认行为)是将嵌套 UI 控件声明为从类外部不可访问。这绝对是个好主意,这样您就可以控制调用者如何从 UI 获取数据,但这也使得很难为控件提供一些已知值来测试该逻辑。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编写/实现 API:可测试性与信息隐藏 的相关文章

  • Oracle Java 教程 - 回答问题时可能出现错误

    我是 Java 新手 正在阅读 Oracle 教程 每个部分之后都有问题和答案 我不明白一个答案中的一句话 见下面的粗体线 来源是https docs oracle com javase tutorial java javaOO QandE
  • Base36 编码字符串?

    我一直在网上查找 但找不到解决此问题的方法 在 Python Ruby 或 Java 中 如何对以下字符串进行 Base 36 编码 nOrG9Eh0uyeilM8Nnu5pTywj3935kW 5 Ruby 以 36 为基数 s unpa
  • HAProxy SSL终止+客户端证书验证+curl/java客户端

    我希望使用我自己的自签名证书在 HAProxy 上进行 SSL 终止 并使用我创建的客户端证书验证客户端访问 我通过以下方式创建服务器 也是 CA 证书 openssl genrsa out ca key 1024 openssl req
  • 文本在指定长度后分割,但不要使用 grails 打断单词

    我有一个长字符串 需要将其解析为长度不超过 50 个字符的字符串数组 对我来说 棘手的部分是确保正则表达式找到 50 个字符之前的最后一个空格 以便在字符串之间进行彻底的分隔 因为我不希望单词被切断 public List
  • 埃拉托色尼筛法 - 实现返回一些非质数值?

    我用 Java 实现了埃拉托斯特尼筛法 通过伪代码 public static void sieveofEratosthenes int n boolean numArray numArray new boolean n for int i
  • 您建议使用哪种压缩(GZIP 是最流行的)servlet 过滤器?

    我正在寻找一个用于大容量网络应用程序的 GZIP servlet 过滤器 我不想使用容器特定的选项 要求 能够压缩响应负载 XML Faster 已在大批量应用的生产中得到验证 应适当设置适当内容编码 跨容器移植 可选择解压缩请求 谢谢 我
  • FileNotFoundException - Struts2 文件上传

    Strange FileNotFoundException使用Struts2上传文件时 这是 JSP 的一部分
  • Java中的断点和逐步调试?

    抱歉我的问题名称很奇怪 我不知道如何寻找这个 因为我不知道这些东西是如何称呼的 Visual Studio 中至少有一个功能 您可以单击代码左侧并设置一个大红点的起点 然后运行程序 您可以通过按 f8 或 f5 实际上是不同的 f 来跟踪步
  • 如何停止执行的 Jar 文件

    这感觉像是一个愚蠢的问题 但我似乎无法弄清楚 当我在 Windows 上运行 jar 文件时 它不会出现在任务管理器进程中 我怎样才能终止它 我已经尝试过 TASKKILL 但它对我也不起作用 On Linux ps ef grep jav
  • Java - 从 XML 文件读取注释

    我必须从 XML 文件中提取注释 我找不到使用 JDOM 或其他东西来让它们使用的方法 目前我使用 Regex 和 FileReader 但我不认为这是正确的方法 您可以使用 JDOM 之类的东西从 XML 文件中获取注释吗 或者它仅限于元
  • 避免 Java 中的重复导入:继承导入?

    有没有办法 继承 导入 Example 常见枚举 public enum Constant ONE TWO THREE 使用此枚举的基类 public class Base protected void register Constant
  • 如何对使用 Controller.User 变量的控制器操作进行单元测试?

    我有一个控制器操作 如果用户已经登录 它会自动重定向到新页面 User Identity IsAuthenticated 针对这种情况编写单元测试以确保重定向发生的最佳方法是什么 我一直在使用以下 Mocks 和 Moq 来允许在我的单元测
  • 无需登录即可直接从 Alfresco 访问文件/内容

    我的场景是这样的 我有一个使用 ALFRESCO CMS 来显示文件或图像的 Web 应用程序 我正在做的是在 Java servlet 中使用用户名和密码登录 alfresco 并且我可以获得该登录的票证 但我无法使用该票证直接从浏览器访
  • 如何在Java中对对象数组进行字段级别排序以进行等级比较?

    In Java Class StudentProgress String Name String Grade CTOR goes here main class main method StudentProgress arrayofObje
  • 如何处理 StaleElementReferenceException

    我正在为鼠标悬停工作 我想通过使用 for 循环单击每个链接来测试所有链接的工作条件 在我的程序中 迭代进行一次 而对于下一次迭代 它不起作用并显示 StaleElementReferenceException 如果需要 请修改代码 pub
  • JMS 中的 MessageListener 和 Consumer 有什么区别?

    我是新来的JMS 据我了解Consumers能够从队列 主题中挑选消息 那么为什么你需要一个MessageListener因为Consumers会知道他们什么时候收到消息吗 这样的实际用途是什么MessageListener 编辑 来自Me
  • 源值 1.5 的错误已过时,将在未来版本中删除

    我使用 scala maven plugin 来编译包含 scala 和 java 代码的项目 我已经将源和目标设置为1 7 但不知道为什么maven仍然使用1 5 这是我在 pom xml 中的插件
  • 如何高效计算连续数的数字积?

    我正在尝试计算数字序列中每个数字的数字乘积 例如 21 22 23 98 99 将会 2 4 6 72 81 为了降低复杂性 我只会考虑 连续的数字 http simple wikipedia org wiki Consecutive in
  • 检查应用程序是否在 Android Market 上可用

    给定 Android 应用程序 ID 包名称 如何以编程方式检查该应用程序是否在 Android Market 上可用 例如 com rovio angrybirds 可用 而 com random app ibuilt 不可用 我计划从
  • 如果找不到指定的图像文件,显示默认图像的最佳方式?

    我有一个普通的电子商务应用程序 我将 ITEM IMAGE NAME 存储在数据库中 有时经理会拼错图像名称 为了避免 丢失图像 IE 中的红色 X 每次显示产品列表时 我都会检查服务器中是否有与该产品相关的图像 如果该文件不存在 我会将其

随机推荐

  • jQuery.proxy() 用法

    我正在阅读有关的 apijQuery proxy 它看起来很有希望 但我想知道在什么情况下最好使用它 谁能启发我吗 当你想要一个具有以下功能的函数时this值绑定到特定对象 例如 在事件处理程序 AJAX 回调 超时 间隔 自定义对象等回调
  • usleep() 计算经过的时间表现得很奇怪

    我使用下面的代码计算每次连续调用处理程序函数所花费的时间 以毫秒为单位 当我使用 usleep 1000 时 即每次调用之间的 1 毫秒时间差为 10 毫秒 而当我使用 usleep 1000000 时 即 1 秒 每次调用之间的时间间隔令
  • 覆盖从另一个模块导入的函数中的全局变量

    假设我有两个模块 a py value 3 def x return value b py from a import x value 4 我的目标是使用以下功能a x in b 但更改函数返回的值 具体来说 value将被查找a作为全局名
  • 删除事件发生时从 Microsoft Graph 获取通知

    我已经订阅了活动 https outlook office com api v2 0 me events 推送通知 当我删除重复主事件的一个事件时 我收到带有主事件 ID 的更新通知 而不是特定发生事件 ID 如果不与所有以前的重复事件进行
  • 使用命名实体训练模型

    我正在使用命名实体识别器查看standford corenlp 我有不同类型的输入文本 我需要将其标记到我自己的实体中 所以我开始训练我自己的模型 但它似乎不起作用 例如 我的输入文本字符串是 Book of 49 Magazine Art
  • Setter.Target 给我一个错误“RelativePanel.AlignHorizo​​ntalCenterWithPanel”

    我正在开发一个 UWP 应用程序 我正在使用 Template10 我有一个TextBlock 在VisualStateNarrow我要它RelativePanel AlignVerticalCenterWithPanel True and
  • 令人惊讶的是,达夫尼未能验证集合理解的有界性

    Dafny 对于集合交集函数的定义没有任何问题 function method intersection A set
  • Android:如何控制主页按钮

    我们正在尝试为我邻居的精神和身体残疾的女儿提供一个应用程序 让她使用 Android 平板电脑作为说话者 也就是说 她按下几个大按钮 设备就会生成语音 该应用程序基本上是一个 WebView 和一个 Javascript 中的附加对象 用于
  • Python - 尽管使用 df.loc 但仍获取“SettingWithCopyWarning”

    尽管使用了推荐的方法 我还是收到了SettingWithCopyWarning 我缺少什么 我该如何纠正它或抑制这个特定的警告 import numpy as np import pandas as pd df pd DataFrame n
  • 如何在 Django 中创建一个查询集来查看数据库中的名称是否是我的查询字符串的子字符串?

    正如标题所提到的 我正在 Django 中工作 并尝试创建一个查询集来返回所有名称值是我的 query string 的子字符串的 客户 模型 我想要这样的东西 Customer objects filter firstName icont
  • 编写 ruby​​gems 的陷阱

    已有问题及答案how to writerubygem 但是在编写 ruby gem 时应该避免什么 什么会给使用您的 ruby gem 的人带来问题 宝石包装 最佳实践给出了很多建议 其中一些包括 不要污染全局加载路径 理想情况下 只有fo
  • SceneKit SCNPhysicsBody 收到休息通知

    SceneKit有没有办法在什么时候收到通知dynamicBody处于休息状态 我想删除dynamicBody当它落到地面并完全停止移动时 我想我会有相当多的那些 所以我想使用基于事件的东西 而不是循环遍历所有bodies并检查它们的速度
  • WinHttpRequest gzip 响应解析

    我在用着MSXML2 XMLHTTP60在我的 VBA 项目中进行 http 冲浪 问题是MSXML2 XMLHTTP60仅限于四个并发请求 我正在尝试使用WinHttp WinHttpRequest 5 1相反 还有另一个问题 MSXML
  • 根据两个数组的差异创建第三个数组

    我需要根据两个数组的差异创建第三个数组 我根本无法理解这个逻辑 正确的第三个数组v3将是 来自下面的代码 v3 Carol Ted Thor Freya Thanks Sub MatchArrays Dim v1 v2 v3 Dim i A
  • 在 VBScript 中设置当前日期和时间的格式

    我想知道是否有人可以帮助我 我对 ASP 很陌生 我想按如下方式设置当前日期和时间的格式 yyyy mm dd hh mm ss 但我能做的就是以下 Response Write Date 有人可以帮我吗 默认情况下 Classic ASP
  • 三星 Galaxy Note III 模拟器设置

    我正在将我的 iPhone 应用程序移植到 Android 客户端使用三星 Galaxy Note III 我需要创建一个模拟器来帮助调试 但在使用我获得的设置启动模拟器时遇到问题gsmarena 类似的帖子也有 不过都是Samsung G
  • php SoapClient 在传递具有相对路径模式的 wsdl 时失败

    我有以下问题 当我向 SoapClient 对象传递一个使用相对路径导入架构的 wsdl 时 它的实例化失败 无论如何 根据我的研究 我相信情况确实如此 我的代码如下 wsdl http myproxy webservice wsdl op
  • 使用有限的操作对双端队列进行排序?

    您好 我在 Robert Sedgewick 的 算法第四版 中遇到了一个问题 出队排序 解释如何对一副牌进行排序 但唯一允许的操作是查看最上面两张牌的值 交换最上面两张牌以及将最上面的牌移动到这副牌的底部 我希望有人能解释一下这是如何完成
  • SQLite 是最适合用于嵌入式数据库的东西吗? [复制]

    这个问题在这里已经有答案了 我需要构建一个将安装在 Linux 服务器上的 Java 应用程序 当人们安装时 他们只需要安装这个应用程序 启动它 仅此而已 但我们有一些数据需要保存 我对MySQL说不 因为它需要服务器 我对 XML 说不
  • 编写/实现 API:可测试性与信息隐藏

    很多时候 我在参与 API 的设计 实现时都面临着这样的困境 我是一个非常坚定的支持者信息隐藏并尝试为此使用各种技术 包括但不限于内部类 私有方法 包私有限定符等 这些技术的问题在于它们往往会妨碍良好的可测试性 虽然其中一些技术可以解决 例