๋ฒ๊ทธ ์ค๋ช
๊ฐ์ง sinon.restore()
( resetHistory
, resetBehavior
๋๋ reset
)๋ฅผ ์ง์ธ ์ ์์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ ์ ์๋ ๋ฐ์ค๋ฅผ ๋ง๋ค๊ณ ์๋ ๋ฐ์ค๋ฅผ ์ง์ฐ๋ ค๊ณ ํ์ต๋๋ค. ์ฃผ์ฌ์๊ฐ ์์ต๋๋ค. ์๋์ ์
๋๊น?
์ฌํํ๋ ค๋ฉด
it("Demonstrates how to restore a fake", () => {
let sandbox = sinon.createSandbox();
let thing = sandbox.fake();
thing("Hi");
expect(thing.calledOnce).to.be.true;
expect(thing.lastArg).to.equal("Hi");
sandbox.restore();
sandbox.resetHistory();
sandbox.resetBehavior();
sandbox.reset();
expect(thing.calledOnce).to.be.false; // Fails. thing.calledOnce === true
expect(thing.lastArg).to.be.undefined; // Fails if above commented out. thing.lastArg === "Hi"
});
์์๋๋ ํ๋
thing.lastArg
๋ undefined
๋๋ null
๋ ๊ฒ์ผ๋ก ์์ํ์ต๋๋ค. ๊ทธ๊ฒ์ ๋์๊ฒ "Hi"
์คํฌ๋ฆฐ ์ท
ํด๋น ์ฌํญ ์์
์ปจํ ์คํธ (๋ค์ ์ ๋ณด๋ฅผ ์์ฑํ์ญ์์ค) :
์ถ๊ฐ ์ปจํ
์คํธ
๋ฐฑ์๋๋ฅผ ํ
์คํธํ๋ ๋ฐ ์ฌ์ฉํ๋ Sinon์ ๊ธฐ๋ฅ์ ํ
์คํธํ๊ธฐ ๋งํ๋ฉด๋ฉ๋๋ค.
ํ์ฌ๋ก์๋ _ ๋ฒ๊ทธ์ฒ๋ผ ๋ณด์ด์ง๋ง _ ์์งํ ์ ๋ ์ด๊ฒ์ ๋ํด ์์ ํ ํ์ ํ์ง ๋ชปํฉ๋๋ค. ๊ฐ์ง์ ๋ํ ์์ ์ ์ด๋ค ์๋ฏธ์์๋ ๋ถ๋ณ์ด์ด์ผํ๋ค๋ ์ ์ ๊ธฐ์ตํ์ง๋ง, ์ํ๊ฐ ์๊ธฐ ๋๋ฌธ์ (์ฆ, ํธ์ถ๋์๋์ง ์๊ณ ์์) ๋ถ๋ณ์ฑ์ ์คํฐ ๋น ๋์์ผ๋ก ๋ง ์ ํ๋์ด์ผํฉ๋๋ค.
@mroderick ์ด ๋๋ตํ๊ธฐ์ ์ ํฉํ ์ฌ๋์ด ๋ ์ ์์ต๋๋ค ...
์ด๊ฒ์ด ์ฌ๋ฐ๋ฅธ ๋์์ด๋ผ๋ฉด ์ต์ํ ๋ฌธ์๋ฅผ ์ ๋ฐ์ดํธํด์ผํฉ๋๋ค.
@ fatso83 ๊ฐ์ฌํฉ๋๋ค! .restore
๋ฌธ์๋ ๊ฐ์ง๋ฅผ ์ธ๊ธํ์ต๋๋ค.
์ด ๋ฒ๊ทธ๋ ๋ ์์ ์ด ์กฐ๊ธ๋ง ์์์ผ๋ฏ๋ก ์์ ํ๋ฉด ์ข์ ๊ฒ์ ๋๋ค.
๋ฉฐ์น ๋์ ๋ค๋ฅธ ์ฌ๋์ด PR์ํ์ง ์์ผ๋ฉด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์๊ฐ์ ์ฐพ์ ์ ์๋์ง ํ์ธํ ์ ์์ต๋๋ค.
๋๋ ์ด๊ฒ์ ์ง๊ธ ์กฐ์ฌํ๊ณ ์์ ํ ์คํธ์ ๊ฐ์ด ๋ฌธ์๋ ์ฝ๊ฐ ์คํด์ ์์ง๊ฐ ์์ต๋๋ค. ์ค์ ๋ก๋ (๊ฑฐ์) ๋งํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด์ง๋ง ๋จผ์ ๋ช ๊ฐ์ง ์ฉ์ด๋ฅผ ์ ์ํ๊ฒ ์ต๋๋ค.
๋ณต์ -์๋ ๋ฐ์ค๋ฅผ ๋ณต์ํ๋ค๋ ๊ฒ์ ๋ชจ๋ ์คํ
๊ธฐ๋ฅ์ ์๋ ์ํ๋ก ๋ณต์ํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์ฆ, ์คํ
์ด ์๋ณธ์ ๋ฎ์ด ์ฐ๋ฉด ๊ฐ ๊ฐ์ฒด์์ ์ ๊ฑฐ๋ฉ๋๋ค.
์ฌ์ค์ -์ฌ์ค์ ์ ์์ฑ ๋ ๊ฐ ํ์ดํฌ์ ์ํ๊ฐ ์ง์์ง์ง ๋ง ๊ฐ์ฒด์๋ ์ฌ์ ํ ์กด์ฌ ํจ์ ์๋ฏธํฉ๋๋ค.
์ ํ ์คํธ์ ๋ฌธ์ ์ ์
๋ง์ด ์๋ผ :-)
๋๋ ํ
์คํธํ๊ณ reset
๋ฐ restore
ํธ์ถ์ด ์ ๋๋ก ์๋ํฉ๋๋ค. ๊ฑฐ์ ... reset
๋ calledOnce
์ ์ํ๋ฅผ ์ ๋ฆฌํ์ง๋ง lastArgument
์ (๋) ์ ๋ฆฌ๋์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค. ์ธ๋ถ ์ฌํญ์ ์์ด ๋ฒ๋ ธ๋์ง ์ข ๋ ํ์ธํฉ๋๋ค.
์ฌํ ํ ์์๋ ๊ฒ์ผ๋ก ์ข
๋ฃํ์ง๋ง fake.lastArg
๋ฒ๊ทธ์ ๋ํ ์๋ก์ด ๋ฌธ์ ๊ฐ ์ด๋ฆฝ๋๋ค.
ํ์ธํ๋ ค๋ฉด : https://runkit.com/fatso83/sinon-issue-2065
์ด ๋ฒ๊ทธ๊ฐ ๋์๊ฒ๋ ๋ฐ์ํ๊ณ ์์์ ํ์ธํ ์ ์์ต๋๋ค. ๊ตฌํ์ด ๊ทธ๋๋ก ์ ์ง๋์ด์ผํ๋ ํ
์คํธ ์๋๋ฆฌ์ค๊ฐ ์์ง๋ง ํจ์๋ ์ด์ ํ
์คํธ์ ์คํ
๊ฒฐ๊ณผ๋ฅผ ์ ๋ฌํฉ๋๋ค. ์ฆ, sandbox.restore()
์ด (๊ฐ) ํ ์์ ์
๋๋ค.
7.5.0
๋ฒ์ ์์ ์ด๋ฐ ์ผ์ด ๋ฐ์ํฉ๋๋ค.
@ oscarr-reyes https://github.com/sinonjs/sinon/commit/54df7f7000a9db2dd05319daa49518f3cd5a5dd7์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋์ง ํ์ธํ ์ ์์ต๋๊น? ์์ง ์ ๋ฆด๋ฆฌ์ค๋ก ๋ง๋ค์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค.
@ oscarr-reyes ์ฌํ ๊ฐ๋ฅํ ์๋ฅผ ๋ง๋ค ์ ์์ต๋๊น? ์ฐ๋ฆฌ๋ ์์ง์ด ๋ฒ๊ทธ๋ฅผ ๋ณด์ง ๋ชปํ์ต๋๋ค. ์์ ์ค๋ ๋์ ์ ์ค๋ช ์ ์ดํด๋ณด๋ฉด ์ฐ๋ฆฌ๊ฐ ๋ฐ๊ฒฌ ํ ์ ์ผํ ๋ฒ๊ทธ๋ ํ๋์ ํ๋๊ทธ๊ฐ ์ง์์ง์ง ์์ ๊ฒ์ ๋๋ค. ๋ค๋ฅธ ๋ชจ๋ ๊ฒ์ด ์๋ํฉ๋๋ค. ๋ฐ๋ผ์ ์ค์ ๋ก ์ฆ๋ช ์ ๋ณด์ง ์๋ ํ ๋ฒ๊ทธ๋ ์์ต๋๋ค (lastArg ํ๋๊ทธ๋ฅผ ์ ์ธํ๊ณ ).
๋๋ ๊ฐ์ ๋ฌธ์ ์ ์ง๋ฉดํ๋ค.
๋ด "validateCaptcha"๋ฏธ๋ค์จ์ด๋ฅผ ์๋ ๋ฐ์ฑํ๊ณ ๋ณต์ ํ ๋ ์คํ ์ ๊ณ์ ๋ฐํํฉ๋๋ค.
/src/middlewares/captcha.js
const request = require("got");
const validateCaptcha = async (req, res, next) => {
// if (process.env.NODE_ENV !== "production") return next();
const captcha = req.body["g-recaptcha-response"] || req.body.g_recaptcha_response;
// g-recaptcha-response is the key that browser will generate upon form submit.
// if its blank or null means user has not selected the captcha, so return the error.
if (captcha === undefined || captcha === "" || captcha === null)
return res
.status(400)
.json({ message: "Please, verify that you are not a robot" });
// Put your secret key here.
const secretKey = process.env.GOOGLE_CAPTCHA_API_SECRET_KEY;
// req.connection.remoteAddress will provide IP address of connected user.
const verificationUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${captcha}`;
try {
// Hitting GET request to the URL, Google will respond with success or error scenario.
const { body } = await request.post(verificationUrl, { responseType: "json" });
// Success will be true or false depending upon captcha validation.
if (body.success !== undefined && !body.success)
return res
.status(400)
.json({ message: "Captcha validation error" });
} catch (error) {
return res
.status(503)
.json({ message: "Couldn't validate captcha" });
}
next();
};
module.exports = {
validateCaptcha,
};
/test/api/user.spec.js
require("../setup.spec");
const supertest = require("supertest");
const captcha = require("../../src/middlewares/captcha");
const sinon = require("sinon");
let app;
let agent;
let sandbox;
const login = (anAgent, userCredentials) =>
anAgent
.post("/api/users/signIn")
.send(userCredentials);
const logout = (anAgent) =>
anAgent
.post("/api/users/signOut");
describe("User endpoints", function () {
this.beforeAll(async () => {
sandbox = sinon.createSandbox();
sandbox
.stub(captcha, "validateCaptcha")
.callsFake(async (req, res, next) => {
console.log("Validate Captcha FAKE");
return next();
});
// sandbox.restore();
});
this.beforeEach(async () => {
app = require("../../src/app");
agent = supertest.agent(app);
const sequelize = require("../../src/models");
await sequelize.sync({ force: true });
const Customer = require("../../src/models/customer");
const User = require("../../src/models/user");
await Customer.create(
{
firstName: "Test",
lastName: "Customer",
user: {
email: "[email protected]",
password: "boo",
},
},
{
include: [User],
}
);
});
this.afterEach(async () => {
sandbox.restore();
});
it("should create a new user", function (done) {
agent
.post("/api/users/signUp")
.send({
firstName: "firstName",
lastName: "lastName",
email: "[email protected]",
password: "aPassword",
})
.expect(201)
.end((err, res) => {
err ? done(err) : done();
});
});
it("should sign in", function (done) {
login(agent, { email: "[email protected]", password: "boo" })
.expect(200)
.end((err, res) => {
const { message, user } = res.body;
message.should.be.equal("valid");
user.should.have.property("email").equal("[email protected]");
user.should.have.property("customer").have.property("firstName").equal("Test");
user.should.have.property("customer").have.property("lastName").equal("Customer");
agent
.get("/api/users/me")
.expect(200)
.end((err, res) => {
const user = res.body;
user.should.have.property("email").equal("[email protected]");
user.should.have.property("customer").have.property("firstName").equal("Test");
user.should.have.property("customer").have.property("lastName").equal("Customer");
err ? done(err) : done();
});
});
});
it("should sign out", async function () {
await login(agent, { email: "[email protected]", password: "boo" });
await logout(agent)
.expect(302)
.expect("Location", "/");
return agent
.get("/api/users/me")
.expect(401);
});
it("should return unauthorized", function (done) {
agent
.get("/api/users/me")
.expect(401)
.end((err, res) => {
err ? done(err) : done();
});
});
});
์๋ํ์ง ์๋์ง ํ์ธํ๊ธฐ ์ํด ๋ด๊ฐํ๋ ์ผ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
1) ์ฒซ ๋ฒ์งธ ํ
์คํธ๋ฅผ ์ํด ๋ชจ์
2) ๋ค์ ํญ๋ชฉ์ ๋ํด ๋ณต์ํ์ญ์์ค.
beforeAll ์์ "sandbox.restore ();"๋ผ๋ ์ฃผ์์ด ์์ต๋๋ค. ๋ด๊ฐ ์๋ํ๊ณ ๊ฑฐ๊ธฐ์์ ์๋ํ์ต๋๋ค ...ํ์ง๋ง ์ธ๋ชจ๊ฐ ์์ต๋๋ค
๋๋ ๋ํ ๋ด app.js์ ๋งค๋ฒ ์๊ตฌํ๊ณ ์์ต๋๋ค.
5 ๋ถ ์ ์ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ์ต๋๋ค.
require("../setup.spec");
const decache = require("decache");
const supertest = require("supertest");
const sequelize = require("../../src/models");
const sinon = require("sinon");
const sandbox = sinon.createSandbox();
let agent;
const login = (anAgent, userCredentials) =>
anAgent
.post("/api/users/signIn")
.send(userCredentials);
const logout = (anAgent) =>
anAgent
.post("/api/users/signOut");
describe("User endpoints", function () {
this.beforeAll(async () => {
});
this.beforeEach(async () => {
decache("../../src/app");
decache("../../src/middlewares/captcha");
const captcha = require("../../src/middlewares/captcha");
sandbox
.stub(captcha, "validateCaptcha")
.callsFake(async (req, res, next) => {
console.log("Validate Captcha FAKE");
return next();
});
const app = require("../../src/app");
agent = supertest.agent(app);
await sequelize.sync({ force: true });
const Customer = require("../../src/models/customer");
const User = require("../../src/models/user");
await Customer.create(
{
firstName: "Test",
lastName: "Customer",
user: {
email: "[email protected]",
password: "boo",
},
},
{
include: [User],
}
);
});
this.afterEach(async () => {
sandbox.restore();
});
it("should create a new user", function (done) {
agent
.post("/api/users/signUp")
.send({
firstName: "firstName",
lastName: "lastName",
email: "[email protected]",
password: "aPassword",
})
.expect(201)
.end((err, res) => {
err ? done(err) : done();
});
});
it("should sign in", function (done) {
login(agent, { email: "[email protected]", password: "boo" })
.expect(200)
.end((err, res) => {
const { message, user } = res.body;
message.should.be.equal("valid");
user.should.have.property("email").equal("[email protected]");
user.should.have.property("customer").have.property("firstName").equal("Test");
user.should.have.property("customer").have.property("lastName").equal("Customer");
agent
.get("/api/users/me")
.expect(200)
.end((err, res) => {
const user = res.body;
user.should.have.property("email").equal("[email protected]");
user.should.have.property("customer").have.property("firstName").equal("Test");
user.should.have.property("customer").have.property("lastName").equal("Customer");
err ? done(err) : done();
});
});
});
it("should sign out", async function () {
await login(agent, { email: "[email protected]", password: "boo" });
await logout(agent)
.expect(302)
.expect("Location", "/");
return agent
.get("/api/users/me")
.expect(401);
});
it("should return unauthorized", function (done) {
agent
.get("/api/users/me")
.expect(401)
.end((err, res) => {
err ? done(err) : done();
});
});
});
์ด ๋ฌธ์ ๋๋ณด๊ณ ์์ต๋๋ค. ์ ์ด๊ฒ์ด ๋ซํ ์์ต๋๊น?
๋ณต์ ๋์์ ์คํ ์ ์ค์ ํ๋ ๋ฐ ์ฌ์ฉ๋๋ ํน์ ๊ตฌ๋ฌธ์ ๋ฏผ๊ฐ ํ ์ ์์ต๋๋ค. Afaik ๋ ๊ฐ์ง ๋ชจ๋์ด ์์ ์ ์ํํ๋ ์ ํจํ ๋ฐฉ๋ฒ์ด์ด์ผํ์ง๋ง restore ()๋ ๋ค๋ฆ ๋๋ค. ํ๋์์๋ ์๋ํ๊ณ ๋ค๋ฅธ ํ๋์์๋ ์๊ธฐ์น ์์ ๊ฒฐ๊ณผ๊ฐ ๋ฐ์ํฉ๋๋ค.
const sinon = require('sinon')
// restore does not work as expected
let db = {
query: () => {
return 'my query 1'
}
}
const dbStub = sinon.stub().resolves('somejson')
db.query = dbStub
db.query() // somejson
sinon.restore()
db.query() // resolves nothing
// restore works as expected
db = {
query: () => {
return 'my query 1'
}
}
sinon.stub(db, 'query').resolves('somejson')
db.query() // somejson
sinon.restore()
db.query() //my query 1
@scottpatrickwright db.query
์ง์ ํ ๋น ํ ๋ sinon
๋ ๋ณต์ํด์ผ ํ ํญ๋ชฉ์ด๋ ํ ๋น ์ ์ ๊ฐ์ด ๋ฌด์์ธ์ง ์ ์ ์์ต๋๋ค.
@scottpatrickwright ์ค๋ ๋์์ ๋ถ๋ช ํ ์ ์ ์๋ฏ์ด ๋ฒ๊ทธ๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋ซํ๋๋ค. ๊ทํ์ ๊ฒฝ์ฐ์ ๊ฐ์ด ์ฌ์ฉ์ ์ค๋ฅ์์ต๋๋ค. ์ค๋ช ๋๊ณ ์๋ ํ๋๋ก ์๋ํฉ๋๋ค. :-)
Sinon ์๋ ๋ฐ์ค๋ ์๊ณ ์๋ ์ ๋ณด๋ฅผ ์ ๋ฆฌํ ์ ์์ต๋๋ค. ์ฆ, Sinon์ ์ํธ ์์ฉํ๋ ๋์์ ๋ํ ์ง์์ด ์์ด์ผํฉ๋๋ค. ์์์ ์ค๋ช
ํ ์คํ
์ ๋
๋ฆฝํ์ผ๋ก ๋ง๋ค์ด์ง๋๋ค. Sinon์ ์ฌ์ฉ์๊ฐ ํ ๋น ํ ๊ฐ์ฒด๋ฅผ ์ธ์ํ์ง ๋ชปํฉ๋๋ค. ๋ ๋ฒ์งธ ์์์ ๋ณ๊ฒฝ๋ฉ๋๋ค. sinon.replace(obj, fieldname, fake)
์ (๋ฅผ) ์ํํ์ฌ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์๋ ์์ต๋๋ค. JavaScript ์ผ๋ฟ์
๋๋ค ๐
์ถฉ๋ถํ ๊ณตํํฉ๋๋ค-์ค๋ช ๊ฐ์ฌํฉ๋๋ค.
2021 ๋
2 ์ 19 ์ผ ๊ธ์์ผ ์ค์ 7:02 Carl-Erik Kopseng [email protected]
์ผ๋ค :
@scottpatrickwright https://github.com/scottpatrickwright ํ์๋์์ต๋๋ค
์ค๋ ๋์์ ๋ช ๋ฐฑํ ๋ฒ๊ทธ๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋ฅ
๊ทํ์ ๊ฒฝ์ฐ์ ๊ฐ์ด ์ฌ์ฉ์ ์ค๋ฅ : ์ค๋ช ๋ฐ ์๋๋๋ก ์๋ํฉ๋๋ค :-)Sinon ์๋ ๋ฐ์ค๋ ์๊ณ ์๋ ์ ๋ณด๋ฅผ ์ ๋ฆฌํ ์ ์์ต๋๋ค. ์ด๋ Sinon์ด
์ํธ ์์ฉํ๋ ๋์์ ๋ํ ์ง์์ ๊ฐ์ต๋๋ค. ์คํ ๋น์
์์ ์ค๋ช ์ ๋ ๋ฆฝํ์ผ๋ก ๋ง๋ค์ด์ง๋๋ค. Sinon์
ํ ๋น ํ ๊ฐ์ฒด. ๋ ๋ฒ์งธ ์์์ ๋ณ๊ฒฝ๋ฉ๋๋ค. ๋น์ ์ ํ ์ ์์ต๋๋ค
๋ํ sinon.replace (obj, fieldname, fake)๋ฅผ ์ํํ์ฌ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ต๋๋ค. ๊ทธ๋ฅ
์๋ฐ ์คํฌ๋ฆฝํธ ๐โ
๋น์ ์ด ์ธ๊ธ ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๊ณ ์์ต๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธํ์ธ์.
https://github.com/sinonjs/sinon/issues/2065#issuecomment-782032440 ๋๋
๊ตฌ๋ ์ทจ์
https://github.com/notifications/unsubscribe-auth/AAKKRTFVMGEYVIB5GSIDXKTS7ZHMRANCNFSM4IGBASQQ
.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ถฉ๋ถํ ๊ณตํํฉ๋๋ค-์ค๋ช ๊ฐ์ฌํฉ๋๋ค.
2021 ๋ 2 ์ 19 ์ผ ๊ธ์์ผ ์ค์ 7:02 Carl-Erik Kopseng [email protected]
์ผ๋ค :