λ²κ·Έ μ€λͺ
Sinonμ μ΅μ v7 릴리μ€λ‘ μ
κ·Έλ μ΄λνλ©΄ μκΈ°μΉ μμ μ€λ₯κ° λ°μν©λλ€. λ΄κ° νμΈν μ μμλ μ μΌν κ΄λ ¨ λ³κ²½ μ¬νμ PR : # 1955- deep-equal.js
λ₯Ό samsam.deepEqual
λ체νμ΅λλ€.
ν
μ€νΈλ 7.2.0κΉμ§μ λͺ¨λ λ²μ μμ ν΅κ³Όνκ³ , 7.1.1 (ν λ¦΄λ¦¬μ€ λ€λ‘)λ‘ λλλ €λ λ¬Έμ κ° λ°μνμ§ μκ³ λͺ¨λ ν
μ€νΈκ° ν΅κ³Όλ©λλ€.
κ΄μ°° λ μ€λ₯ :
AssertionError: expected addTracks to have been called with arguments [{ language: "en", trackContentType: "CEA608" }]
[[functionStub] { language: "en", trackContentType: "CEA608" }] [{ language: "en", trackContentType: "CEA608" }]
ν μ€νΈ μμ± μ€λ₯ :
it('should call getTextTracksManager, Track, and setCaptionLang', () => {
sinonSandbox.stub(chromecastReceiver, 'setCaptionLang');
chromecastReceiver.player.getTextTracksManager = sinonSandbox.stub()
.returns({ addTracks: sinonSandbox.spy() });
sinonSandbox.stub(cast.framework.messages, 'Track').returns(() => ({}));
chromecastReceiver.loadCaptions('off');
expect(chromecastReceiver.player.getTextTracksManager).to.have.callCount(1);
expect(cast.framework.messages.Track).to.have.been
.calledWith(3, cast.framework.messages.TrackType.TEXT);
expect(chromecastReceiver.textTracksManager.addTracks).to.have.been.calledWith([{
trackContentType: cast.framework.messages.CaptionMimeType.CEA608,
language: 'en',
}]);
expect(chromecastReceiver.setCaptionLang).to.have.been.calledWith('off');
});
μ¬ννλ €λ©΄
λμμ μ¬ννλ λ¨κ³ :
μμλλ νλ
ν
μ€νΈλ₯Ό ν΅κ³Όν΄μΌν©λλ€.
μ€ν¬λ¦° μ·
ν΄λΉλλ κ²½μ° λ¬Έμ λ₯Ό μ€λͺ
νλ λ° λμμ΄λλ μ€ν¬λ¦° μ·μ μΆκ°ν©λλ€.
컨ν μ€νΈ (λ€μ μ 보λ₯Ό μμ±νμμμ€) :
μ΄μ λν μμΈν μ 보λ₯Ό μ 곡 ν μ μλμ§ μλ €μ£Όμμμ€.
μΌλ°μ μΌλ‘ νλ₯ν λ²κ·Έ λ³΄κ³ μμ κ°μ¬λ립λλ€. νμ§λ§ ν κ°μ§ ν° κ²°ν¨μ΄ μμ΅λλ€. κ²μ¦μ΄λ λ²κ·Έ μΆμ μ μν΄ κ·νμ μμ λ₯Ό μ€νν μ μμ΅λλ€! μ체μ μΌλ‘ μ€νν μ μμΌλ©° μ¬ν λ°©λ²μ΄ μμΌλ©΄μ΄λ₯Ό κ³ μΉκΈ°κ° μ΄λ ΅μ΅λλ€.
μ΅μνμ λ²μ ν보λ₯Ό λ§λ€ μ μμ΅λκΉ? var myObject = { aMethod: function(){}, aProp: 42 }; // etc
μ κ°μ λ¨μνμ²λΌ μ 체 μλ³Έ μμ λ₯Ό 볡μ ν νμκ° μμ΅λλ€.
μ΄κ²μ΄ κ³ μ₯λ¬λ€λ κ²μ μμλ€λ©΄ λ¬Όλ‘ λ©μ΄μ λ²μ μ μ¬λ Έμ κ²μ΄λ―λ‘ νκ· ν μ€νΈκ° μ’μ κ²μ λλ€!
μΆμ : sinon-chai
λ κ΄λ ¨λ κ² κ°μ΅λλ€. κ·Έκ²μ λμ΄ ν κ°μΉκ° μμ΅λλ€.
@ fatso83 볡μ λ₯Ό μν΄ λΆλ¦¬λκ³ μ€ν κ°λ₯ν μμ λ₯Ό μ»λ λ°©λ²μ λ³΄κ² μ΅λλ€.
μ λ°μ΄νΈκ° μμ΅λκΉ?
μ΄ λ¬Έμ λ μ΅κ·Ό νλμ΄ μμκΈ° λλ¬Έμ μλμΌλ‘ μ€λλ κ²μΌλ‘ νμλμμ΅λλ€. λ μ΄μ νλμ΄ λ°μνμ§ μμΌλ©΄ νμλ©λλ€. κ·νμ κΈ°μ¬μ κ°μ¬λ립λλ€.
@mantoni νλ‘μ νΈμμ μ΄λ° μΌμ΄ λ°μνλ€κ³ λ³΄κ³ νμ§ μμμ΅λκΉ?
μ λ°μ΄νΈκ° μμ΄μ μ£μ‘ν©λλ€. λ μ΄μ λ¬Έμ κ° ν¬ν¨ λ νλ‘μ νΈμ μ°κ²°λμ΄ μμ§ μμ΅λλ€. λ€λ₯Έ μ¬λμ΄ κ°μ λ¬Έμ λ₯Ό κ²½νν μ μλ€λ©΄ μ¬νμ μλ₯Ό μ 곡νλ©΄ μ’μ κ²μ λλ€.
@ fatso83 κ°μ λ¬Έμ κ° λ°μν κΈ°μ΅μ΄ μμ΅λλ€. λͺ κ°μ§ νλ‘μ νΈμμ λ¬Έμ μμ΄ μ΅μ SinonμΌλ‘ μ κ·Έλ μ΄λνμ΅λλ€.
μ¬ν ν μ μμμΌλ‘ λ«κΈ°
μ΄ λ¬Έμ λ₯Ό μ£½μμμ λμ΄λ € μ μ£μ‘νμ§λ§ κ°μ λ¬Έμ μ λ΄μ°©νμ΅λλ€.
_ μλ μ½λλ μ€λͺ μ μν΄ λ¨μνλμμ΅λλ€ ._
λ΄ μμ© νλ‘κ·Έλ¨μλμ΄ μμ μ΄ μμ΅λλ€.
class MyUser {
// ... some user properties
}
λμ€μ μ΄μ κ°μ Koa 컨νΈλ‘€λ¬ κΈ°λ₯μμ μ¬μ©ν©λλ€.
const mqClient = require('./mqClient');
// GET /userDetails/<id> endpoint
async function getUserDetailsController(req, res) {
const user = new MyUser();
// populate user with properties etc.
user.id = '1';
user.name = 'Hans Gruber';
user.tasks = [{ id: '42', title: 'execute Nakatomi tower heist' }];
// ... get more details ...
// Notify other users about this user
mqClient.notifyOthers(user);
res.json({ ok: true, user });
}
κ·Έλ¦¬κ³ λ΄ getUserDetailsController.test.js
λ€μκ³Ό κ°μ΄ν©λλ€.
const mqClient = require('./mqClient');
const createExpectedUser = overrides => {
id: '1',
name: 'Hans Gruber',
tasks: [{ id: '42', title: 'execute Nakatomi tower heist' }],
...overrides
};
it('does not fail', async () => {
sinon.spy(mqClient, 'notifyOthers');
const expectedUser = createExpectedUser();
await presetRequest // helper object to run the controller code
.get('/userDetails/1')
.expect(200);
// This works with sinon 7.1.1.
// This fails with sinon 7.2.0 and onwards.
sinon.assert.calledWithMatch(mqClient.notifyOthers, expectedUser);
});
λλ μ΄κ²μ sinon 7.1.1 ( samsam
μ "2.1.3"μ μ¬μ©ν¨)μΌλ‘ ν
μ€νΈνκ³ , μλν©λλ€.
λ²μ 7.2.0μ΄ μ€ν¨ν©λλ€ ( samsam
3.3.3 μ¬μ©).
μ΅μ λ²μ 9.2.3λ μ€ν¨ν©λλ€ ( samsam
5.3.0 μ¬μ©).
μ λ Node 14.5.3μ μμ§λ§ μ΄κ²μ Node 12.20.0μμλ λ°μν©λλ€.
컨νΈλ‘€λ¬μ user
μ expectedUser
λ₯Ό ν΅ν΄ ν
μ€νΈμμ JSON.stringify
μλμΌλ‘ λΉκ΅νλλ° 100 % λμΌν©λλ€. ꡬ문 μ μ μΌν μ°¨μ΄μ μ 컨νΈλ‘€λ¬λ ν΄λμ€λ₯Ό μ¬μ©νλ λ°λ©΄ ν
μ€νΈλ κ°μ²΄ 리ν°λ΄μ μ¬μ©νλ€λ κ²μ
λλ€.
μ μ΄λ° μΌμ΄ λ°μνλμ§ μ’μ μ¬λμ΄ μμ΅λκΉ? μλλ©΄μ΄ λ¬Έμ λ₯Ό ν΄κ²°νλ λ°©λ²μ λν μ’μ μμ΄λμ΄κ° μμ΅λκΉ?
κ°μ¬ν©λλ€.
λ€λμ
μ΄λ₯Ό νμΈν μμλ μ¬ν κ°λ₯ν μ¬λ‘κ°μλ ν,μ΄ λ¬Έμ λ₯Ό μ‘°μ¬νλ λ° λ¬΄λ£ μκ°μ 보λ΄λ κ²μ μ λΉν ν μ μμ΅λλ€. λλ ΅μ΅λλ€. λλ μ°Ύμ κ²μ΄μκ³ λ΄κ° μ³λ€λ κ²μ μ¦λͺ ν κ²λ μμ κ²μ λλ€.
κ·Έλλ λ²κ·Έλ₯Ό κ³ μΉλ κ²μ μ’μνλ―λ‘ _ λ΄κ° μ€νν μμλ _ 무μΈκ°λ₯Ό λ§λλ λ° μκ°μ ν μ ν μ μλ€λ©΄ κ·Έλ κ²νμΈμ! RunKitμμ΄λ₯Όμν νλ₯ν μλΉμ€μ λλ€ : https://runkit.com/fatso83/sinon-issue-reproducible-bug-template
μλ νμΈμ @ fatso83 ,
μκ°μλ΄μ΄ μ‘°μ¬ν΄ μ£Όμ
μ κ°μ¬ν©λλ€. λλ λΉμ μ΄ μμ² νλλ‘ λ²κ·Έλ₯Ό 보μ¬μ£Όλ λ°ν· λ°λͺ¨λ₯Ό λ§λ€μμ΅λλ€. λ€λ₯Έ require('sinon')
λ¬Έμ μ£Όμ / μ£Όμ ν΄μ νκ³ μ½λλ₯Ό μ€ννμ¬ λ²κ·Έλ₯Ό νμΈνμμμ€.
https://runkit.com/danielkg/sinon-issue-reproducible-bug-template
μ΄λ€ μ΄μ λ‘ λ runkitμ΄ μλνμ§ μλ κ²½μ°λ₯Ό λλΉνμ¬ μ¬κΈ°μ μ½λλ₯Ό κ²μν©λλ€.
// Employs 'mini-mocha' to emulate running in the Mocha test runner (mochajs.org)
require("@fatso83/mini-mocha").install();
// const sinon = require('[email protected]'); // WORKS!
const sinon = require('[email protected]'); // FAILS!
// const sinon = require('sinon'); // FAILS!
const { assert } = require('@sinonjs/referee');
const SAMPLE_USER = {
id: 1,
name: 'Kid',
age: 22,
weight: 83.5,
notes: [{ txt: 'abc' }, { txt: 'def' }, { txt: 'ghi' }],
};
class MyUser {
constructor(id, name, age, weight, notes = []) {
this.id = id;
this.name = name;
this.age = age;
this.weight = weight;
this.notes = notes;
}
}
const createFakeUser = overrides => ({
id: '007',
name: 'Bond',
...overrides,
});
class Talkie {
talk(user, topic) {
console.log('talked to', user.name, 'about', topic);
}
}
const action = (userId, talkie) => {
const { name, age, weight, notes } = SAMPLE_USER;
const user = new MyUser(userId, name, age, weight, notes);
talkie.talk(user, 'apples');
return user;
}
describe('stubbing', () => {
const talkie = new Talkie();
afterEach(() => {
sinon.restore();
});
beforeEach(() => {
sinon.spy(talkie, 'talk');
});
it('is the same user', () => {
const userA = action(1, talkie);
const userB = createFakeUser(SAMPLE_USER);
assert.equals(JSON.stringify(userA), JSON.stringify(userB));
});
it('should set the return value', () => {
const userA = action(1, talkie);
const userB = createFakeUser(SAMPLE_USER);
sinon.assert.calledWith(talkie.talk, userB, sinon.match.string);
});
});
μκ°μ΄μμ λ λ€μ ν λ²λ³΄μΈμ. κ°μ¬ν©λλ€.
κ°μ¬ν©λλ€.
λ€λμ
κ°μ₯ μ μ©ν λκΈ
μλ νμΈμ @ fatso83 ,
μκ°μλ΄μ΄ μ‘°μ¬ν΄ μ£Όμ μ κ°μ¬ν©λλ€. λλ λΉμ μ΄ μμ² νλλ‘ λ²κ·Έλ₯Ό 보μ¬μ£Όλ λ°ν· λ°λͺ¨λ₯Ό λ§λ€μμ΅λλ€. λ€λ₯Έ
require('sinon')
λ¬Έμ μ£Όμ / μ£Όμ ν΄μ νκ³ μ½λλ₯Ό μ€ννμ¬ λ²κ·Έλ₯Ό νμΈνμμμ€.https://runkit.com/danielkg/sinon-issue-reproducible-bug-template
μ΄λ€ μ΄μ λ‘ λ runkitμ΄ μλνμ§ μλ κ²½μ°λ₯Ό λλΉνμ¬ μ¬κΈ°μ μ½λλ₯Ό κ²μν©λλ€.
μκ°μ΄μμ λ λ€μ ν λ²λ³΄μΈμ. κ°μ¬ν©λλ€.
κ°μ¬ν©λλ€.
λ€λμ