2017-07-25 63 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類似的基於屬性的工具爲您生成隨機值。

相關問題