2012-03-01 58 views
76

我正在學習Node.js,並且一直在玩Express。真的很喜歡這個框架;但是,我很難弄清楚如何編寫路由的單元/集成測試。單元測試如何使用Express路由?

能夠單元測試簡單的模塊很容易,並已與Mocha;然而,我的單元測試Express失敗,因爲我傳入的響應對象不保留這些值。

路由的作用下測試(路由/ index.js):

exports.index = function(req, res){ 
    res.render('index', { title: 'Express' }) 
}; 

單位測試模塊:

var should = require("should") 
    , routes = require("../routes"); 

var request = {}; 
var response = { 
    viewName: "" 
    , data : {} 
    , render: function(view, viewData) { 
     viewName = view; 
     data = viewData; 
    } 
}; 

describe("Routing", function(){ 
    describe("Default Route", function(){ 
     it("should provide the a title and the index view name", function(){ 
     routes.index(request, response); 
     response.viewName.should.equal("index"); 
     }); 

    }); 
}); 

當運行此,它失敗爲「錯誤:檢測到全局泄漏:viewName,data「。

  1. 我在哪裏出錯了,以便我可以得到這個工作?

  2. 有沒有更好的方法讓我在這個級別單元測試我的代碼?

更新 1.更正代碼段,因爲我最初忘記 「它()」。

回答

18

更改您的回覆對象:

var response = { 
    viewName: "" 
    , data : {} 
    , render: function(view, viewData) { 
     this.viewName = view; 
     this.data = viewData; 
    } 
}; 

,它會工作。

+1

謝謝。知道這是簡單的事情。 – JamesEggers 2012-03-01 14:41:18

18

測試HTTP與表達最簡單的方式是竊取TJ's http helper

personally use his helper

it("should do something", function (done) { 
    request(app()) 
    .get('/session/new') 
    .expect('GET', done) 
}) 

如果你想專門測試你的路由對象,然後在正確的嘲笑通過

describe("Default Route", function(){ 
    it("should provide the a title and the index view name", function(done){ 
     routes.index({}, { 
      render: function (viewName) { 
       viewName.should.equal("index") 
       done() 
      } 
     }) 
    }) 
}) 
+5

你能修復'幫手'鏈接嗎? – 2012-10-23 13:49:51

+0

@NicholasMurray [test-server](https://github.com/Raynos/test-server) – Raynos 2012-10-23 20:25:22

+14

HTTP單元測試的最新方法似乎是使用[supertest](https:// github .com/visionmedia/supertest)。這也似乎TJ的http幫手已經發展到超級。 – 2013-03-26 19:17:56

26

正如其他人在評論中推薦的那樣,它看起來像測試Express控制器的標準方式是通過supertest

的試驗例可能是這樣的:

describe('GET /users', function(){ 
    it('respond with json', function(done){ 
    request(app) 
     .get('/users') 
     .set('Accept', 'application/json') 
     .expect(200) 
     .end(function(err, res){ 
     if (err) return done(err); 
     done() 
     }); 
    }) 
}); 

潛在上升空間:您可以在一個去測試整個堆棧。

下行:感覺和行爲有點像集成測試。

+1

我喜歡這個,但是有沒有一種斷言viewName的方法(就像在原始問題中那樣) - 或者我們必須斷言響應的內容? – Alex 2013-12-02 23:51:31

+10

我同意你的缺點,這不是單元測試。這依賴於你所有單元的集成來測試你的應用程序的url。 – 2014-08-03 15:55:58

+4

我認爲說「路線」實際上是一個「集成」是合法的,也許測試路線應留給集成測試。我的意思是,匹配到其定義的回調的路由的功能大概已經由express.js測試過;任何用於獲取路線最終結果的內部邏輯,理想情況下都應該模塊化,並且這些模塊應該進行單元測試。他們的互動,即路線,應該進行整合測試。你會同意嗎? – 2017-03-06 12:33:53

15

我得出結論,真正單元測試快速應用程序的唯一方法是在請求處理程序和核心邏輯之間保持很多分離。因此,您的應用程序邏輯應該位於可以是require d和單元測試的單獨模塊中,並且對Express請求和響應類的依賴性最小。

然後在請求處理程序中,您需要調用核心邏輯類的相應方法。

當我完成重組我的當前應用程序後,我會舉個例子!

我想像this?(隨意分叉的要點或評論,我仍然在探索)。

編輯

這裏有一個小例子,內聯。有關更詳細的示例,請參見the gist

/// usercontroller.js 
var UserController = { 
    _database: null, 
    setDatabase: function(db) { this._database = db; }, 

    findUserByEmail: function(email, callback) { 
     this._database.collection('usercollection').findOne({ email: email }, callback); 
    } 
}; 

module.exports = UserController; 

/// routes.js 

/* GET user by email */ 
router.get('/:email', function(req, res) { 
    var UserController = require('./usercontroller'); 
    UserController.setDB(databaseHandleFromSomewhere); 
    UserController.findUserByEmail(req.params.email, function(err, result) { 
     if (err) throw err; 
     res.json(result); 
    }); 
}); 
3

若與快遞4注意此示例單元測試從gjohnson

var express = require('express'); 
var request = require('supertest'); 
var app = express(); 
var router = express.Router(); 
router.get('/user', function(req, res){ 
    res.send(200, { name: 'tobi' }); 
}); 
app.use(router); 
request(app) 
    .get('/user') 
    .expect('Content-Type', /json/) 
    .expect('Content-Length', '15') 
    .expect(200) 
    .end(function(err, res){ 
    if (err) throw err; 
    }); 
0

我想知道這是好,但專門爲單元測試,而不是集成測試。這是我在做什麼,現在,

test('/api base path', function onTest(t) { 
    t.plan(1); 

    var path = routerObj.path; 

    t.equals(path, '/api'); 
}); 


test('Subrouters loaded', function onTest(t) { 
    t.plan(1); 

    var router = routerObj.router; 

    t.equals(router.stack.length, 5); 
}); 

凡routerObj只是{router: expressRouter, path: '/api'}。 然後,我使用var loginRouterInfo = require('./login')(express.Router({mergeParams: true}));加載子路由器,然後快速應用程序調用以快速路由器爲參數的init函數。然後initRouter調用router.use(loginRouterInfo.path, loginRouterInfo.router);來安裝子路由器。

的subrouter可以與被測試:

var test = require('tape'); 
var routerInit = require('../login'); 
var express = require('express'); 
var routerObj = routerInit(express.Router()); 

test('/login base path', function onTest(t) { 
    t.plan(1); 

    var path = routerObj.path; 

    t.equals(path, '/login'); 
}); 


test('GET /', function onTest(t) { 
    t.plan(2); 

    var route = routerObj.router.stack[0].route; 

    var routeGetMethod = route.methods.get; 
    t.equals(routeGetMethod, true); 

    var routePath = route.path; 
    t.equals(routePath, '/'); 
}); 
+3

這看起來很有趣。你是否有更多缺失作品的例子來展示這一切如何融合在一起? – cjbarth 2016-08-03 20:29:26

1

爲了實現單元測試,而不是集成測試,我嘲笑請求處理器的響應對象。

/* app.js */ 
import endpointHandler from './endpointHandler'; 
// ... 
app.post('/endpoint', endpointHandler); 
// ... 

/* endpointHandler.js */ 
const endpointHandler = (req, res) => { 
    try { 
    const { username, location } = req.body; 

    if (!(username && location)) { 
     throw ({ status: 400, message: 'Missing parameters' }); 
    } 

    res.status(200).json({ 
     location, 
     user, 
     message: 'Thanks for sharing your location with me.', 
    }); 
    } catch (error) { 
    console.error(error); 
    res.status(error.status).send(error.message); 
    } 
}; 

export default endpointHandler; 

/* response.mock.js */ 
import { EventEmitter } from 'events'; 

class Response extends EventEmitter { 
    private resStatus; 

    json(response, status) { 
    this.send(response, status); 
    } 

    send(response, status) { 
    this.emit('response', { 
     response, 
     status: this.resStatus || status, 
    }); 
    } 

    status(status) { 
    this.resStatus = status; 
    return this; 
    } 
} 

export default Response; 

/* endpointHandler.test.js */ 
import Response from './response.mock'; 
import endpointHandler from './endpointHander'; 

describe('endpoint handler test suite',() => { 
    it('should fail on empty body', (done) => { 
    const res = new Response(); 

    res.on('response', (response) => { 
     expect(response.status).toBe(400); 
     done(); 
    }); 

    endpointHandler({ body: {} }, res); 
    }); 
}); 

然後,實現集成測試,你可以嘲笑你的endpointHandler並調用端點supertest

相關問題