2016-08-27 20 views
5

Tôi có một ứng dụng Node.js sử dụng nhanh 4 và đây là bộ điều khiển của tôi:Làm thế nào để đơn vị kiểm tra một hàm gọi một hàm khác trả về một lời hứa?

var service = require('./category.service'); 

module.exports = { 
    findAll: (request, response) => { 
    service.findAll().then((categories) => { 
     response.status(200).send(categories); 
    }, (error) => { 
     response.status(error.statusCode || 500).json(error); 
    }); 
    } 
}; 

Nó gọi dịch vụ của tôi mà trả về một lời hứa. Tất cả mọi thứ hoạt động nhưng tôi gặp khó khăn khi cố gắng để đơn vị kiểm tra nó.

Về cơ bản, tôi muốn đảm bảo rằng dựa trên những gì dịch vụ của tôi trả về, tôi xóa phản hồi bằng đúng mã trạng thái và nội dung.

Vì vậy, với mocha và Sinon nó trông giống như sau:

it('Should call service to find all the categories', (done) => { 
    // Arrange 
    var expectedCategories = ['foo', 'bar']; 

    var findAllStub = sandbox.stub(service, 'findAll'); 
    findAllStub.resolves(expectedCategories); 

    var response = { 
     status:() => { return response; }, 
     send:() => {} 
    }; 
    sandbox.spy(response, 'status'); 
    sandbox.spy(response, 'send'); 

    // Act 
    controller.findAll({}, response); 

    // Assert 
    expect(findAllStub.called).to.be.ok; 
    expect(findAllStub.callCount).to.equal(1); 
    expect(response.status).to.be.calledWith(200); // not working 
    expect(response.send).to.be.called; // not working 
    done(); 
}); 

Tôi đã thử nghiệm các tình huống tương tự của tôi khi chức năng Tôi đang thử nghiệm trở lại bản thân một lời hứa kể từ khi tôi có thể treo khẳng định của tôi trong đó.

Tôi cũng đã cố gắng bọc bộ điều khiển.findAll with a Promise và giải quyết nó từ response.send nhưng nó không hoạt động.

+1

Mọi chức năng gọi hàm trả lời hứa là không đồng bộ và phải trả lại lời hứa. Nếu bạn chỉ gọi lại, bạn phải quay lại thử nghiệm API dựa trên cuộc gọi lại. – Bergi

+0

Bạn đang sử dụng Chai? Nếu vậy, http://chaijs.com/plugins/chai-as-promised/ –

+0

Có nhưng chức năng tôi đang thử nghiệm không trả về chuỗi lời hứa con trai như đã hứa sẽ không giúp đỡ – jbernal

Trả lời

6

Bạn nên di chuyển phần khẳng định của bạn thành phương pháp res.send để đảm bảo tất cả các nhiệm vụ async được thực hiện trước khi khẳng định:

var response = { 
    status:() => { return response; }, 
    send:() => { 
    try { 
     // Assert 
     expect(findAllStub.called).to.be.ok; 
     expect(findAllStub.callCount).to.equal(1); 
     expect(response.status).to.be.calledWith(200); // not working 
     // expect(response.send).to.be.called; // not needed anymore 
     done(); 
    } catch (err) { 
     done(err); 
    } 
    }, 
}; 
+0

Có, bây giờ nó hoạt động. Tuy nhiên, nếu tôi thử kiểm tra thì nó không thành công (so sánh trạng thái với 204, giả sử), nó không hiển thị xác nhận không thành công. Nó chỉ lần ra và nói: 'Lỗi: thời gian chờ của 2000ms vượt quá. Đảm bảo thực hiện gọi lại() gọi lại trong thử nghiệm này.' – jbernal

+0

@jbernal xấu của tôi, quên rằng bạn phải nắm bắt lỗi được ném và chuyển nó đến 'done', xem câu trả lời đã chỉnh sửa –

+0

Cảm ơn rất nhiều. Sử dụng các khối try/catch đã lưu tôi. – phillyslick

1

Ý tưởng ở đây là phải có lời hứa service.findAll() trả về có thể truy cập bên trong mã của thử nghiệm mà không cần gọi số service. Theo như tôi có thể thấy sinon-as-promised mà bạn có thể sử dụng không cho phép làm như vậy. Vì vậy, tôi chỉ sử dụng một bản địa Promise (hy vọng phiên bản nút của bạn không phải là quá cũ cho nó).

const aPromise = Promise.resolve(expectedCategories); 
var findAllStub = sandbox.stub(service, 'findAll'); 
findAllStub.returns(aPromise); 

// response = { .... } 

controller.findAll({}, response); 

aPromise.then(() => { 
    expect(response.status).to.be.calledWith(200); 
    expect(response.send).to.be.called;  
}); 
+0

Tôi biết kịch bản này. Nhưng nếu bạn nhận ra, 'controller.findAll' của tôi không trả lại lời hứa, nó không cần nó. Và do đó, vấn đề của tôi. Tôi không thể thực hiện một cái gì trong đó. – jbernal

+0

@jbernal, tôi thấy quan điểm của bạn. Tôi đã trộn lẫn 'findAll's. Sẽ không ích gì nếu bạn gọi 'controller.findAll()' và sau chuỗi đó 'service.findAll()', tức là 'constoller.findAll(); service.findAll(). Sau đó (() => { mong đợi (....) }); ' –

+0

Đừng nghĩ vậy ...' controller.findAll' là lệnh gọi 'service.findAll' và đó là một trong những điều được thử nghiệm, vì vậy tôi không muốn gọi nó một cách rõ ràng trong đó. – jbernal

1

Khi mã rất khó để kiểm tra nó có thể chỉ ra rằng có thể khác nhau khả năng thiết kế để khám phá, giúp thúc đẩy việc kiểm tra dễ dàng. Điều gì nhảy ra là service được đính kèm trong mô-đun của bạn và sự phụ thuộc không hề bị lộ ra. Tôi cảm thấy như mục tiêu không nên tìm cách kiểm tra mã AS IS của bạn mà là tìm ra một thiết kế tối ưu.

IMO Mục tiêu là tìm cách để lộ service để kiểm tra của bạn có thể cung cấp khả năng triển khai được phân tích, để logic findAll có thể được kiểm tra một cách đồng bộ, đồng bộ.

Một cách để thực hiện việc này là sử dụng thư viện như mockery hoặc rewire. Cả hai là khá dễ sử dụng, (trong kinh nghiệm của tôi mockery bắt đầu suy giảm và trở nên rất khó khăn để duy trì như là bộ thử nghiệm của bạn và số lượng các mô-đun phát triển) Họ sẽ cho phép bạn vá var service = require('./category.service'); bằng cách cung cấp đối tượng dịch vụ của riêng bạn với riêng của mình findAll xác định .

Một cách khác là rearchitect mã của bạn để lộ service cho người gọi, theo một cách nào đó. Điều này sẽ cho phép người gọi của bạn (bài kiểm tra đơn vị) cung cấp bản khai service của riêng mình.

Một cách dễ dàng để thực hiện việc này là xuất khẩu một hàm contstructor thay vì một đối tượng.

module.exports = (userService) => { 

    // default to the required service 
    this.service = userService || service; 

    this.findAll = (request, response) => { 
    this.service.findAll().then((categories) => { 
     response.status(200).send(categories); 
    }, (error) => { 
     response.status(error.statusCode || 500).json(error); 
    }); 
    } 
}; 

var ServiceConstructor = require('yourmodule'); 
var service = new ServiceConstructor(); 

Bây giờ thử nghiệm có thể tạo ra một stub cho service và cung cấp nó vào ServiceConstructor để thực hiện phương pháp findAll. Loại bỏ sự cần thiết cho một bài kiểm tra không đồng bộ hoàn toàn.

+0

Cảm ơn bạn rất nhiều vì đã trả lời. Đây chắc chắn là những ý tưởng tôi cần xem xét. Tôi đang làm việc trên ứng dụng nút đầu tiên của mình và tôi chắc chắn có nhiều mẫu cấu trúc tối ưu và tốt hơn mà tôi có thể làm theo để cải thiện mã của mình. Tôi sẽ cố gắng thực hiện chúng. Chúc mừng – jbernal

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