2012-09-21 78 views
59

如何在我的node.js應用程序中嘲笑數據庫,在這種情況下,使用mongodb作爲博客REST API的後端?在node.js中模擬數據庫?

當然,我可以將數據庫設置爲特定的testing數據庫,但我仍然會保存數據,而不是僅測試我的代碼,而且還測試數據庫,所以實際上我沒有進行單元測試,而是進行集成測試。
那麼應該怎麼做?創建數據庫包裝器作爲應用程序和數據庫之間的中間層,並在測試時替換DAL?

// app.js 
var express = require('express'); 
    app = express(), 
    mongo = require('mongoskin'), 
    db = mongo.db('localhost:27017/test?auto_reconnect'); 

app.get('/posts/:slug', function(req, res){ 
    db.collection('posts').findOne({slug: req.params.slug}, function (err, post) { 
     res.send(JSON.stringify(post), 200); 
    }); 
}); 

app.listen(3000); 

// test.js 
r = require('requestah')(3000); 
describe("Does some testing", function() { 

    it("Fetches a blogpost by slug", function(done) { 
    r.get("/posts/aslug", function(res) { 
     expect(res.statusCode).to.equal(200); 
     expect(JSON.parse(res.body)["title"]).to.not.equal(null); 
     return done(); 
    }); 

    }); 
)); 

回答

85

我不認爲數據庫相關的代碼可以在不與數據庫軟件測試它正確地進行測試。這是因爲你正在測試的代碼不僅僅是javascript而且是數據庫查詢字符串。即使在你的情況下,查詢看起來很簡單,但你不能依賴它永遠那樣。

因此,任何數據庫仿真層都必須實現整個數據庫(可能還不包括磁盤存儲)。那時,即使您稱之爲單元測試,您最終也會對數據庫模擬器進行集成測試。另一個缺點是數據庫模擬器可能最終會產生與數據庫不同的一組錯誤,您可能最終不得不爲數據庫仿真器和數據庫編寫代碼(類似於IE,Firefox和Chrome等的情況)。 )。

因此,在我看來,正確測試代碼的唯一方法是將其與真實數據庫連接。

+1

你知道,你說得很好。雖然單元測試具有驚人的目的(即隔離),但您已經爲集成測試提供了強有力的支持。 –

+3

@MichaelPerrenoud:我喜歡christkv的回答規定的規則:**「不要嘲笑任何你不擁有的東西」**。雖然沒有詳細說明爲什麼這是一個壞主意,但要記住這是一個簡單的規則。 – slebetman

+1

我不同意這個答案,他們在meteorjs運行測試時(我假設它不是一個模擬庫,而是一個臨時文件)以某種方式設置了一個測試數據庫,它非常方便。讓一個物體的行爲與mongodb完全一樣並在其後清理是非常有用的。無論是內存還是臨時文件都是實現細節,因此您不必重複代碼。我同意做駕駛員的人應該是製造模擬物體的人。 – Uri

36

有一個一般的經驗法則,當談到嘲諷這是

不要嘲笑任何你不擁有。

如果你想模擬數據庫隱藏,它會拋出一個抽象的服務層,並模擬該層。然後確保您集成測試實際服務層。

就我個人而言,我已經不再使用mock進行測試,並且使用它們進行頂部到底部的設計,幫助我從頂部到底部驅動開發,嘲笑服務層,並隨後最終實現這些層並編寫集成測試。作爲一種測試工具,他們往往會讓你的測試變得非常脆弱,在最糟糕的情況下會導致實際行爲和模擬行爲之間的分歧。

+0

當然,你可以隱藏它在存儲庫或網關後面,並使用模擬來驅動你的測試驅動方法,並隔離你的單元測試...你是什麼意思,你不使用模擬測試呢?你會保持這個嘲弄的網關/存儲庫仍然在你的測試中,然後以某種方式使用一個接口來通過接口指定你的存儲庫中的真實實現嗎? – PositiveGuy

3

我用任何語言對單元測試DB代碼進行單元測試的首選方法是通過存儲庫抽象訪問Mongo(這裏有一個例子http://iainjmitchell.com/blog/?p=884)。實現將根據數據庫特定的功能而有所不同,但通過從您自己的邏輯中刪除所有Mongo代碼,您可以進行單元測試。簡單地用一個殘缺的版本來取代Mongo Repository實現,這是非常簡單的。例如,只需將對象存儲在簡單的內存字典集合中即可。

如果沒有數據庫依賴性,您將獲得單元測試自己的代碼的好處,但仍需要對主數據庫執行集成測試,因爲您可能永遠無法模擬真實的特質數據庫如其他人在這裏所說的。我發現的事情與在安全模式下進行索引和沒有安全模式一樣簡單。具體來說,如果你有一個唯一的索引,你的虛擬內存實現可能會在所有情況下都遵守,但是Mongo不會沒有安全模式。

因此,雖然您仍然需要針對某些操作的數據庫進行測試,但您仍然可以使用殘缺的存儲庫實現單元測試自己的邏輯。

+0

但在某些時候,您真正的實現代碼必須引用實際的數據調用。我假設你正在將接口注入到您的存儲庫中以實現實際的數據層查詢代碼? – PositiveGuy

+0

嘗試使用Docker。我通過使用Docker運行容器,運行使用特定測試數據初始化數據庫的容器,解決了數據庫和數據包安裝中多年的惡夢配置問題。事實上,我運行3個容器的堆棧:一個帶有DB,一個帶有應用程序代碼,另一個帶有測試驅動程序。如果您的數據集規模適中,您甚至可以啓動這些堆棧的並行實例,大大縮短您的測試周期。 – Raidex

32

我不同意選擇的答案或迄今爲止的其他答覆。

如果您可以捕獲混淆所產生的錯誤,並且在獲取QA之前對DB模式和您的代碼進行了多次混亂的更改,這難道不是很棒嗎?我敢打賭,大多數人會大聲吶喊!

你絕對可以並且應該隔離和測試你的DB模式。而且你不是基於模擬器或重型圖像或DB和機器的娛樂。就像SQLite這樣的東西就是一個例子。你可以根據內存中的輕量級實例來運行它,並且在內存實例中使用靜態數據不會改變,這意味着你真正在測試你的數據庫,你也可以相信你的測試。顯然它很快,因爲它在內存中,是一個骨架,並在測試運行結束時被廢棄。

所以是的,你應該和你應該測試導出到你使用的任何數據庫引擎/運行時的內存實例中的一個非常輕量級的SCHEMA,並添加一個非常少量的靜態數據成爲你孤立的嘲笑D B。

您可以定期(以自動方式)從真實數據庫中導出實際模式,並在每次推送QA之前將這些數據導入/更新到內存數據庫實例中,然後您會立即知道是否有任何最新數據庫更改由您最近更改模式的數據庫管理員或其他開發人員打破了任何測試。

雖然我讚揚盡力回答我的努力,但如果可以的話,我會降低當前的答案,但我是新的,並且尚未建立足夠的聲望,但尚未建立我的能力。

至於回覆「不要嘲笑你不擁有的東西」的人。我想他的意思是說「不要測試你不擁有的東西」。但你嘲笑你不擁有的東西!因爲這些是未經測試的東西需要被隔離!

我打算與你分享這個HOW,並且將在未來的時間點用真實的JS代碼更新這篇文章!

這就是許多測試驅動團隊一直在做的事情。你只需要瞭解如何。

+8

任何更新如何? – ChickenWing24

+3

我很樂意提供,但如果它不能緩解問題並提供解決方案,我不會。請在有機會時更新您的帖子。 – Shanimal

2

我有這個困境,選擇與測試數據庫一起工作,並在每次測試開始時清理它。 (如何刪除所有內容:https://stackoverflow.com/a/25639377/378594

使用NPM,您甚至可以創建一個測試腳本來創建數據庫文件並在之後清理它。

+0

這實際上是一個很好的方法,我會建議任何人做嚴重的CI/CD流水線操作。利用今天的JavaScript工具,我們能夠在運行測試之前和之後輕鬆創建/刪除測試數據庫。我會爭論這種方法在開發時是否合適(您不想在每次代碼更改時刪除和創建數據庫),但還有其他解決方案。至少它解決了必須維護單獨的插件集成模擬數據並將它們粘貼到測試環境的問題。 – Nicky

4

什麼?!爲什麼要嘲笑一切呢?嘲笑的目的是跳過複雜性和單元測試自己的代碼。如果你想寫e2e測試,然後使用數據庫。

編寫代碼來設置/拆卸單元測試的測試數據庫是技術債務,令人難以置信的不滿意。

有在故宮模擬庫:

蒙戈 - https://www.npmjs.com/package/mongomock

獴 - https://www.npmjs.com/package/mockgoose

如果那些不支持你需要的功能,然後是你可能需要使用真實的東西。

+1

哈哈「mockgoose」前5最有趣的NPM包名imo –

+0

恕我直言,最好是模擬比模擬 - https://jsmockito.org/ –