2017-07-25 36 views
0

我正在尝试学习TDD,同时编写一个脚本,将其输入数据转换为一系列长函数。无论我是用python还是R编写它,问题都是相似的。我认为它与TDD理解更相关。如何为一系列数据转换编写单元测试?

# Look of main in python 
def main(): 
    data = get_data() 
    data_a = transform_fun1(data) 
    data_b = transform_fun2(data_a) 
    data_c = transform_fun3(data_b) 
    .... 
    return data_x 

# Look of main in R 
main <- function() { 
    data <- get_data() %>% 
     transform_fun1() %>% 
     transform_fun2() %>% 
     transform_fun3() %>% 
     ... 
    data_x 
} 

什么写每个transform_fun单元测试的最好的过程,知道他们需要输入以前transform_fun的结果呢?

一开始它看起来相当干净,但随着我越来越深入,我开始在每个测试中重现越来越多的main,这种测试并没有好闻。复制main过程的整个部分看起来与单元测试的想法不直观。

# in python (pytest) 
def test_transform_fun_n(data): 
    data_a = transform_fun1(data) 
    data_b = transform_fun2(data_a) 
    ... 
    data_n = transform_fun_n(data_n-1) 
    assert data_n == blabla 

# in R (testthat) 
test_that("transform_fun_n do what I expect", { 
    data_a <- transform_fun1(data) 
    data_b <- transform_fun2(data_a) 
    ... 
    data_n <- transform_fun_n(data_n-1) 
    expect_that(data_n, equals(blabla)) 
}) 

我也尝试在每一步之间添加夹具(至少在python中),但它看起来并不理想。

- 编辑 - 试图勾画出VoiceOfUnreason的答案。

def transformV1(data): 
    return data + x 

def transformV2(data): 
    return transformV1(data) + y 

def transformV3(data): 
    return transformV2(data) + z 

def main(): 
    data = get_data() 
    return transformV3(data) 
+0

*我开始在每次测试中重现越来越多的main * - 在main()中是否有比您的代码示例中显示的调用序列更复杂的逻辑? – guillaume31

+0

目前我设置了一些值,但没有任何值不能放入transformVx(数据)函数中的一个。 – xav

回答

0

在开始的时候看起来还算干净,但我越走越搞定了,我开始重现的主越来越多的在每个测试,这并不好闻。复制主流程的整个部分看起来与单元测试的想法不直观。

是的,你说得对。代码试图告诉你,你的规范(和你的生产代码)是在错误的抽象层次上编写的。

def test_transformV1(data, expected): 
    actual = transformV1(data) 
    assert actual == expected 

def main(): 
    data = getData() 
    return transformV1(data) 

当需求发生变化,你写一个测试,采用新规范在这里

def test_transformV2(data, expected): 
    actual = transformV2(data) 
    assert actual == expected 

def test_transformV1(data, expected): 
    actual = transformV1(data) 
    assert actual == expected 

def main(): 
    data = getData() 
    return transformV2(data) 

主要观点在于(一)通过你的产品代码提供单元测试的运动功能(b)新的需求意味着一个新的功能 - 新功能可以用其他功能来实现,但测试只是检查新功能是否返回正确的结果。

如果主要是难以测试(用于an imperative shell一个共同的问题),那么你希望把它作为,你可能可以。

做起来很简单,有没有明显的缺陷

转换的长链需要从外壳到内核重构;给出一个名字,等等。

你的意思是代码应写入更像是我的问题

是末尾添加,这就是思路:必须壳访问使用相同的入口点为功能核心其中一项测试。

+0

非常感谢您的回答。我仍然有点困惑。你的意思是代码应该写得更像我在问题结尾添加的内容(代码在注释中看起来没有格式化)。 – xav

0

由于您已经确定了一系列转换函数,因此逻辑操作过程是单独测试它们。另一方面,测试main()只要它仍然是一个简单的调用序列,就具有可疑价值。

因为每个函数都将前一个函数的结果作为输入,所以您可能会试图将它们链接到测试执行中,就像它们链接在主程序中一样。但是,这种方式会使测试方法失败,并且可能让您将注意力集中到最终结果的快乐路径上,从而阻止您在流程的每个步骤中为输入值探查可能存在问题的边缘情况。

相反,尝试依靠您的函数输入类型来提出一系列测试,这些测试具有不同的输入值,以反映每个函数的各种场景。您甚至可以使用QuickCheck类似的基于属性的工具为您生成随机值。

相关问题