我有一個功能負責收集一堆配置,並從所有這些部分中做出更大的配置。因此,它基本上是:如何使此功能可測試?
let applyUpdate updateData currentState =
if not (someConditionAbout updateData) then
log (SomeError)
let this = getThis updateData currentState.Thingy
let that = getThat updateData currentState.Thingy
let andThat = createThatThing that this updateData
// blablablablablabla
{ currentState with
This = this
That = that
AndThat = andThat
// etc. }
我現在有getThis
,getThat
,createThatThing
,但不適用於applyUpdate
單元測試。我不想重新測試getThis
等等在做什麼,我只想測試applyUpdate
特定的邏輯,僅僅是存根getThis
。在面向對象的風格中,這些將通過依賴注入通過接口傳遞。在實用的風格,我不確定要如何進行:
// This is the function called by tests
let applyUpdateTestable getThisFn getThatFn createThatThingfn etc updateData currentState =
if not (someConditionAbout updateData) then
log (SomeError)
let this = getThisFn updateData currentState.Thingy
// etc
{ currentState with
This = this
// etc. }
// This is the function that is actually called by client code
let applyUpdate = applyUpdateTestable getThis getThat etc
這似乎庶子注射液在功能上等同,但除此之外,我主要關心的是:
- 現在我的代碼更難以遵循,因爲你不能只將F12(去 定義)轉換成函數;這個問題也存在於 OO依賴注入中,但是通過加工來緩解(即Resharper Go To Implementation)。
- 我測試的功能是不是技術上的一個由生產代碼調用(有可能是在貼圖錯誤)
- 我甚至不看一個好名字的功能 的說,「可測試」版本
- 我污染的一切
如何應對函數式編程這些問題的重複定義的模塊?
我愛你的網站btw!我仍然對此有一些擔憂: - 您仍然無法從寫入邏輯的位置(「核心」)開始F12。現在,您已經將具體功能移到了不同的模塊中,因此更難以遵循。但你確實解決了污染問題。 - 我很好奇「標準」做法是什麼;用這種風格編寫的開源F#代碼的任何例子? – Asik
謝謝!確實,你不知道傳遞給核心函數的函數是什麼(就像在OO中傳遞接口時一樣)。我從來沒有理解自己需要「goto實現」,但是你可以在覈心'applyUpdate'實現上「找到所有引用」,然後在使用它的調用位置找到傳遞給哪些函數它。這很好,我希望:) – Grundoon
關於良好實踐的例子,機會是他們不會以與您的例子完全相同的方式書寫。 「核心」代碼往往是小功能,只能做一件事,然後組合在一起。例如「createRecord getThis <*> getThat <*> createThatThing」。我不知道你是否看過「monadster」談話(https://fsharpforfunandprofit.com/monadster/),但提出了一些這樣的想法。 – Grundoon