2012-03-01 38 views
76

Tôi đang trong quá trình học Node.js và đang phát xung quanh với Express. Thực sự thích khung công tác, tuy nhiên, tôi đang gặp khó khăn trong việc tìm ra cách viết một bài kiểm tra đơn vị/tích hợp cho một tuyến đường.Làm thế nào để một đơn vị kiểm tra các tuyến đường với Express?

Có thể thử nghiệm các mô-đun đơn giản dễ dàng và đã thực hiện nó với Mocha; tuy nhiên, kiểm tra đơn vị của tôi với Express không thành công vì đối tượng phản hồi mà tôi truyền vào không giữ lại các giá trị.

Route-Function Under Test (tuyến/index.js):

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

Unit Test Module:

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"); 
     }); 

    }); 
}); 

Khi tôi chạy này, nó không thành công cho "Lỗi: phát hiện rò rỉ toàn cầu: viewName, dữ liệu ".

  1. Tôi làm cách nào để tôi có thể làm việc này?

  2. Có cách nào tốt hơn để tôi kiểm tra mã của tôi ở cấp này không?

Cập nhật 1. Đoạn Corrected đang kể từ khi tôi bước đầu quên "nó()".

Trả lời

18

Thay đổi đối tượng trả lời của bạn:

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

Và nó sẽ làm việc.

+1

Cảm ơn. Biết nó là một cái gì đó đơn giản. – JamesEggers

18

Cách đơn giản nhất để kiểm tra HTTP với tốc là để ăn cắp TJ's http helper

tôi personally use his helper

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

Nếu bạn muốn đặc biệt kiểm tra lộ trình của bạn đối tượng, sau đó vượt qua trong mocks đúng

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

bạn có thể sửa liên kết 'trợ giúp' không? –

+0

@NicholasMurray [test-server] (https://github.com/Raynos/test-server) – Raynos

+14

Dường như cách tiếp cận cập nhật hơn để kiểm tra đơn vị HTTP là sử dụng [supertest] (https: // github .com/visionmedia/supertest) của Visionmedia. Có vẻ như helper http của TJ đã phát triển thành supertest. –

26

Khi những người khác đã đề xuất trong nhận xét, có vẻ như cách kinh điển để kiểm tra bộ điều khiển Express là qua supertest.

Một thử nghiệm ví dụ có thể trông như thế này:

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() 
     }); 
    }) 
}); 

Ưu điểm: bạn có thể kiểm tra toàn bộ stack của bạn trong một đi.

Nhược điểm: cảm giác và hoạt động giống như thử nghiệm tích hợp.

+1

Tôi thích điều này, nhưng có cách nào khẳng định tên khung nhìn (như trong câu hỏi ban đầu) hay chúng ta phải khẳng định về nội dung của phản hồi? – Alex

+10

Tôi đồng ý với nhược điểm của bạn, đây không phải là thử nghiệm đơn vị. Điều này dựa trên sự tích hợp của tất cả các đơn vị của bạn để kiểm tra các url của ứng dụng của bạn. –

+4

Tôi nghĩ rằng nó là hợp pháp để nói rằng một "tuyến đường" thực sự là một 'hội nhập', và có lẽ các tuyến đường thử nghiệm nên được để lại cho các bài kiểm tra tích hợp. Tôi có nghĩa là, chức năng của các tuyến đường phù hợp với callback được xác định của họ có lẽ đã được thử nghiệm bởi express.js; bất kỳ logic nội bộ nào để nhận được kết quả cuối cùng của một tuyến đường, lý tưởng nên được mô đun hóa bên ngoài nó, và các mô-đun đó phải được kiểm tra đơn vị. Tương tác của họ, tức là tuyến đường, nên được tích hợp thử nghiệm. Bạn có đồng ý không? –

15

Tôi đã đi đến kết luận rằng cách duy nhất để thực sự đơn vị ứng dụng thử nghiệm thể hiện là duy trì rất nhiều sự tách biệt giữa các trình xử lý yêu cầu và logic cốt lõi của bạn.

Do đó, logic ứng dụng của bạn phải nằm trong các mô-đun riêng biệt có thể là require d và đơn vị được kiểm tra và có sự phụ thuộc tối thiểu vào các lớp Yêu cầu và phản hồi nhanh như vậy.

Sau đó, trong trình xử lý yêu cầu, bạn cần gọi các phương thức thích hợp của các lớp logic cốt lõi của bạn.

Tôi sẽ đưa ví dụ lên một khi tôi đã hoàn tất việc tái cơ cấu ứng dụng hiện tại của mình!

Tôi đoán một cái gì đó như this? (Cảm thấy tự do để ngã ba ý chính hoặc nhận xét, tôi vẫn đang khám phá điều này).

Sửa

Dưới đây là một ví dụ nhỏ, inline. Xem the gist để biết ví dụ chi tiết hơn.

/// 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

nếu đơn vị thử nghiệm với tốc 4 lưu ý ví dụ này từ 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

tôi đã tự hỏi này là tốt, nhưng đặc biệt cho kiểm tra đơn vị và không kiểm tra hội nhập. Đây là những gì tôi đang làm ngay bây giờ,

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); 
}); 

Nơi routerObj chỉ là {router: expressRouter, path: '/api'}. Sau đó tôi tải trong các chương trình con với var loginRouterInfo = require('./login')(express.Router({mergeParams: true})); và sau đó ứng dụng tốc hành gọi một hàm init tham gia vào bộ định tuyến tốc hành làm tham số. Các initRouter sau đó gọi router.use(loginRouterInfo.path, loginRouterInfo.router); để gắn kết subrouter.

Các subrouter có thể được thử nghiệm với:

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

Điều này trông rất thú vị. Bạn có nhiều ví dụ về các phần còn thiếu để cho thấy tất cả điều này phù hợp với nhau không? – cjbarth

1

Để đạt được kiểm tra đơn vị thay vì thử nghiệm tích hợp, tôi chế giễu đối tượng phản ứng của trình xử lý yêu cầu.

/* 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); 
    }); 
}); 

Sau đó, để đạt được thử nghiệm tích hợp, bạn có thể thử endpointHandler của bạn và gọi cho thiết bị đầu cuối với supertest.

Các vấn đề liên quan