2013-06-09 31 views
8

Tôi đang sử dụng Mocha với Sinon để kiểm tra đơn vị mô-đun node.js của tôi. Tôi đã chế nhạo thành công các phụ thuộc khác (các mô-đun khác mà tôi đã viết), nhưng tôi đã gặp phải các vấn đề nảy sinh các chức năng không thuần túy (như Math.random()Date.now()). Tôi đã thử những điều sau đây (đơn giản hóa để câu hỏi này không được bản địa hóa), nhưng Math.random() không bị bướng bỉnh vì một vấn đề phạm vi rõ ràng. Các phiên bản Math độc lập giữa tệp thử nghiệm và mymodule.js.Stubbing Date.now() và Math.random()

test.js

var sinon = require('sinon'), 
    mymodule = require('./mymodule.js'), 
    other = require('./other.js'); 

describe('MyModule', function() { 
    describe('funcThatDependsOnRandom', function() { 
     it('should call other.otherFunc with a random num when no num provided', function() { 
      sinon.mock(other).expects('otherFunc').withArgs(0.5).once(); 
      sinon.stub(Math, 'random').returns(0.5); 

      funcThatDependsOnRandom(); // called with no args, so should call 
             // other.otherFunc with random num 

      other.verify(); // ensure expectation has been met 
     }); 
    }); 
}); 

Vì vậy, trong ví dụ contrived này, functThatDependsOnRandom() sẽ trông như thế:

mymodule.js

var other = require('./other.js'); 

function funcThatDependsOnRandom(num) { 
    if(typeof num === 'undefined') num = Math.random(); 

    return other.otherFunc(num); 
} 

Có thể còn sơ khai Math.random() trong kịch bản này với Sinon?

Trả lời

7

vâng, đây là một câu hỏi cũ nhưng nó là hợp lệ. Đây là một câu trả lời có hiệu quả, mặc dù tôi rất muốn nghe những gợi ý về cách làm cho nó tốt hơn.

Cách tôi xử lý điều này trong trình duyệt là tạo đối tượng proxy. Ví dụ, bạn không thể stub đối tượng cửa sổ trong trình duyệt để bạn có thể tạo một đối tượng proxy được gọi là windowProxy. Khi bạn muốn có được vị trí bạn tạo một phương thức trong windowProxy gọi là vị trí trả về hoặc đặt windowLocation. Sau đó, khi thử nghiệm, bạn thử windowProxy.location.

Bạn có thể làm điều tương tự với Node.js, nhưng nó không hoạt động đơn giản như vậy. Phiên bản đơn giản là một mô-đun không thể gây rối với không gian tên riêng của mô-đun khác.

Giải pháp là sử dụng mô-đun mockery. Sau khi khởi tạo chế độ nhạo báng, nếu bạn gọi require() với thông số khớp với những gì bạn đã nói với chế độ nhạo báng, nó sẽ cho phép bạn ghi đè câu lệnh require và trả lại các thuộc tính của riêng bạn.

CẬP NHẬT: Tôi đã tạo một ví dụ mã đầy đủ chức năng. Đó là trên Github at newz2000/dice-tddavailable via npm./END CẬP NHẬT

Các tài liệu là khá tốt, vì vậy tôi khuyên bạn nên đọc chúng, nhưng đây là một ví dụ:

Tạo một file randomHelper.js với nội dung như thế này:

module.exports.random = function() { 
    return Math.random(); 
} 

Sau đó, trong mã của bạn cần số ngẫu nhiên, bạn:

var randomHelper = require('./randomHelper'); 

console.log('A random number: ' + randomHelper.random()); 

Mọi thứ sẽ hoạt động như bình thường. Đối tượng proxy của bạn hoạt động giống như Math.random.

Điều quan trọng cần lưu ý là câu lệnh yêu cầu phải chấp nhận một thông số duy nhất, './randomHelper'. Chúng ta sẽ cần lưu ý điều đó.

Bây giờ trong thử nghiệm của bạn, (Tôi đang sử dụng mocha và chai ví dụ):

var sinon = require('sinon'); 
var mockery = require('mockery') 
var yourModule; // note that we didn't require() your module, we just declare it here 

describe('Testing my module', function() { 

    var randomStub; // just declaring this for now 

    before(function() { 
    mockery.enable({ 
     warnOnReplace: false, 
     warnOnUnregistered: false 
    }); 

    randomStub = sinon.stub().returns(0.99999); 

    mockery.registerMock('./randomHelper', randomStub) 
    // note that I used the same parameter that I sent in to requirein the module 
    // it is important that these match precisely 

    yourmodule = require('../yourmodule'); 
    // note that we're requiring your module here, after mockery is setup 
    } 

    after(function() { 
    mockery.disable(); 
    } 

    it('Should use a random number', function() { 
    callCount = randomStub.callCount; 

    yourmodule.whatever(); // this is the code that will use Math.random() 

    expect(randomStub.callCount).to.equal(callCount + 1); 
    } 
} 

Và đó là nó. Trong trường hợp này, cuống của chúng tôi sẽ luôn trả về 0.0.99999; Tất nhiên bạn có thể thay đổi nó.

+0

Câu trả lời hay. Bạn có thể sử dụng proxyquire thay vì nhạo báng quá. – Wtower

0

Bạn có chắc chắn rằng không chế giễu Math là sự cố. Dường như dòng này chắc không có nhiều ý nghĩa:

sinon.mock(other).expects('otherFunc').withArgs(0.5).once(); 

bạn nhạo báng others trong một module nhưng sử dụng nó trong một số khác. Tôi không nghĩ rằng bạn sẽ nhận được phiên bản giả lập trong mymodule.js. Mặt khác, Math.random sẽ làm việc, vì đây là toàn cầu cho tất cả các mô-đun.

Ngoài ra hãy xem điều này SO để phụ thuộc chế nhạo trong các thử nghiệm nodeJS.

+0

Sửa lỗi nếu tôi sai, nhưng tôi nghĩ rằng các mô-đun được lưu trong bộ nhớ cache sau khi có yêu cầu. Vì vậy, 'require ('./ other.js')' trong bộ thử nghiệm và trong mã đang được kiểm thử nên là cùng một ví dụ. Với suy nghĩ này, tôi cho rằng (giống như 'Math.random') chế nhạo' other' trong một cái sẽ thay đổi đối tượng trong cái kia. Nhưng điều đó có thể không hoạt động bởi vì nó gán một đối tượng mới cho đối tượng khác thay vì thay thế các thuộc tính. Bất kỳ cách nào bạn biết với tội lỗi của việc này? –

1

Hãy thử:

sinon.stub(Math, "random", function(){ 
    return 0.5; 
    }); 
+2

Điều này không hoạt động khi hàm sử dụng Math.random() nằm trong một mô-đun khác. Nó hoạt động khi bạn đang sử dụng REPL hoặc chức năng trong cùng một tệp .js. – newz2000

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