【问题标题】:Unit testing curried functions单元测试柯里化函数
【发布时间】:2019-12-07 08:01:12
【问题描述】:

我正在尝试找出正确的思考方式。我正在尝试在 python 中使用一些函数式编程范例。我知道它不是函数式语言,但概念可以翻译。

假设我有一组函数,例如:

def add(x: int, y: int) -> int:
  return x + y

add_one = partial(add, y=1)
add_two = partial(add, y=2)
add_three = partial(add, y=3)

fut = lambda x: add_three(add_one(add_two(x)))

问题是,我是否对被测函数 (fut) 进行单元测试?我会对add_oneadd_twoadd_three 进行单元测试吗?

在我看来,add_oneadd 的一部分,这是一个实现细节,但是如果我“完整”地覆盖了add,那么进行单元测试会感觉很奇怪。

让我们将其扩展一分钟,并将其扩展到更现实的系统。

class Save(Protocol):
  def __call__(self, x: int): ...

def save_to_disk(x: int, file_name:str) -> None:
  """
  Implementation of saving something to a disk
  """
  ...

save: Save = partial(save_to_disk, file_name="random_file")

def add(x: int, y: int) -> int:
  return x + y

add_one = partial(add, y=1)

fut: Save = lambda x: save(add_one(x))

我现在有一个函数来处理 IO 和一些生成要保存的数据的工作。我在这里对fut 进行单元测试吗?我是否认为柯里化只是实现细节并将其留给集成测试?

我对此感到有些迷茫,因为在 OO 世界中,我知道如何使用模拟对其中的一些链接进行单元测试,但它似乎是倒退的,并且违背了一些功能范式以及我正在积极尝试避免的事情玩弄它。

我现在的直觉是,柯里化函数是集成测试,只要所有内容都经过单独测试,显示组件工作,而不是链,我可以留给集成测试,我可以证明整个系统工作正常。

【问题讨论】:

  • 您不需要单独对部分应用的柯里化函数进行单元测试。但是,一旦您编写了此类功能(无论是否部分应用),我都会考虑另一个单元的结果并对其进行测试。
  • 你在哪里画线我想这将是我的后续问题。如果子函数具有复杂的逻辑,则似乎有很多复制单元测试。而不是像我那样组合函数,我也可以嵌套函数。我一直在尝试接受Save 作为函数参数,并将实现设置为默认值,这样我就可以在测试时将其存根。这就是你处理函数链的方式吗?
  • 我实际上不会画一条线:如果你对一个柯里化函数进行单元测试,你不需要测试这个函数的部分应用变体。但是,如果您组合函数 - 组合我的意思是组合两个函数以产生另一个函数,那么您也需要测试这个结果函数。因此,接受函数参数的高阶函数必须使用可能传递给它的任何函数进行单元测试。

标签: python unit-testing functional-programming


【解决方案1】:

关于你的第一个例子:

add_one = partial(add, y=1)
add_two = partial(add, y=2)
add_three = partial(add, y=3)

fut = lambda x: add_three(add_one(add_two(x)))

假设 add_oneadd_twoadd_three 仅在本地用于计算 fut,我认为无需测试这些函数:它们本质上只是对 add 的调用,而您已经测试。如果没有柯里化,代码将只包含三个调用 add 和一些常量参数。

而且,由于这一切仍然很简单,为这些柯里化函数设置单独的测试没有多大价值。如果您要设置一些更复杂的中间函数,情况可能会有所不同,我们称之为bigger_helper。这样的功能可能值得单独测试,以分而治之的方式减少整体测试工作。 (请注意,这与测试私有方法有关,有些人强烈反对。我没有那么严格,并认为在某些情况下测试私有方法可以更好地在测试目标之间进行整体权衡。)

关于您的第二种情况,我同意必须在集成测试中处理它,但我不确定我是否正确理解了您的推理。对我来说,这不是单元测试的候选者,原因如下:该函数不包含任何计算,而只包含交互。因此,当您考虑可以在隔离代码中找到哪些错误时(即,在模拟实际写入磁盘时),在我看来似乎没有。错误更像是,您是否使用正确的参数以正确的顺序调用 IO 操作,并且这是否都适合文件系统的结构等:所有这些问题您无法在使用模拟的隔离代码中回答,因为您将实现根据您的(可能是错误的)假设进行模拟。因此,您可以在此处跳过单元测试,并在该场景中直接进行集成测试。

而且,集成测试的这种推理与柯里化没有任何关系,而只是与缺乏计算有关。

【讨论】:

    【解决方案2】:

    如果add 已经过测试,那么我认为每个部分应用程序都不需要它自己的单元测试。但是,我会通过模拟或监视add_oneadd_twoadd_three 来对fut 进行单元测试(这对于一个体面的测试框架来说应该不难),并确保使用正确的参数调用它们.

    所以,我的单元测试会期望 add_two 被调用时使用任何 fut 调用,add_one 调用时使用任何 add_two 返回值,并且 add_three 调用时使用任何 add_two 返回值。

    【讨论】:

      猜你喜欢
      • 2016-01-01
      • 2021-02-07
      • 2012-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      相关资源
      最近更新 更多