Descreva o bug
Não é possível limpar as falsificações sinon.restore()
(nem resetHistory
, resetBehavior
, nem reset
). Em seguida, tentei criar uma nova caixa de areia e limpá-la. Sem dados. Isso é intencional?
Reproduzir
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"
});
Comportamento esperado
Eu esperava que thing.lastArg
fosse undefined
ou null
. Isso me dá "Hi"
Capturas de tela
N / D
Contexto (preencha as seguintes informações):
Contexto adicional
Apenas testando os recursos do Sinon enquanto o uso para testar um back-end
Do jeito que está, _parece_ um bug, mas para ser honesto, não estou totalmente certo sobre isso. Lembro-me de que um ponto-chave sobre falsificações é que deveriam ser imutáveis em algum sentido, mas como têm estado (ou seja, sabem se foram chamados), suponho que essa imutabilidade deve ser restrita apenas ao comportamento de stub.
@mroderick pode ser a pessoa certa para responder a isso ...
Se este for o comportamento correto, devemos pelo menos atualizar os documentos.
@ fatso83 Muito .restore
mencionou falsificações:
daqui .
Eu mesmo fui mordido por esse bug, então seria bom ser corrigido.
Se ninguém mais fizer uma RP para ele nos próximos dias, posso ver se encontro tempo para corrigi-lo
Eu olhei para isso agora e os documentos são um pouco enganosos, assim como o teste acima. Na verdade, parece fazer (quase) o que diz, mas vamos definir alguns termos primeiro:
restore - restaurar uma sandbox significa restaurar todas as funções stub para seu estado original, o que significa que os stubs são removidos de cada objeto onde eles sobrescreveram o original.
redefinir - redefinir significa que o estado de cada uma das falsificações criadas foi apagado, mas eles ainda estão presentes nos objetos.
O problema com o teste acima é que
Isso não faz sentido :-)
Eu testei e ambas as chamadas reset
e restore
funcionam como deveriam. Quase ... reset
limpa o estado de calledOnce
, mas lastArgument
parece não ter sido limpo. Checando um pouco mais para ver se perdi algum detalhe.
Fechando isto como não reproduzível, mas abrindo um novo problema no bug fake.lastArg
.
Para verificar: https://runkit.com/fatso83/sinon-issue-2065
Posso confirmar que esse bug também está acontecendo comigo, tenho um cenário de teste que requer que a implementação esteja intacta, mas a função carrega o resultado do stub de um teste anterior, o que significa que sandbox.restore()
não está fazendo o que pretendia fazer.
Isso está acontecendo comigo na versão 7.5.0
@ oscarr-reyes Você pode verificar se https://github.com/sinonjs/sinon/commit/54df7f7000a9db2dd05319daa49518f3cd5a5dd7 corrige para você? Acho que ainda não foi lançado.
@oscarr-reyes Você é capaz de fazer um exemplo reproduzível? Ainda não vimos esse bug. Se você olhar para o tópico acima, e minha explicação, você verá que o único bug que encontramos foi um único sinalizador que não foi apagado. Tudo o resto funciona. Portanto, a menos que realmente vejamos alguma prova, não há bug (além do sinalizador lastArg).
Eu tenho enfrentado o mesmo problema.
Estou colocando em sandbox meu middleware "validateCaptcha" e quando restauro ele continua emitindo o stub.
/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();
});
});
});
O que estou fazendo para verificar se não funciona é:
1) Zombe para o primeiro teste
2) Restaure para os seguintes
Dentro de beforeAll há um comentário "sandbox.restore ();" que eu tentei e lá funcionou ... mas é inútil
Eu também estou exigindo cada vez que meu app.js
Cinco minutos atrás eu modifiquei para:
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();
});
});
});
Também estou vendo esse problema. Por que está fechado?
Pode ser que o comportamento da restauração seja sensível à sintaxe específica usada para configurar o stub. Afaik ambos _devem_ ser formas válidas de fazer isso, mas o restore () é diferente. Em um funciona e no outro tem resultados inesperados.
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 quando você faz atribuições diretamente para db.query
, então sinon
não tem como saber o que precisa ser restaurado ou qual era o valor antes da atribuição.
@scottpatrickwright Está fechado porque não há bug, como deve ser aparente no tópico. Foi apenas um erro do usuário, como no seu caso: funciona conforme descrito e pretendido :-)
A caixa de areia Sinon pode limpar coisas que conhece. Isso significa que a Sinon deve ter conhecimento dos objetos com os quais interage. O esboço que você descreve acima é independente. A Sinon não tem conhecimento de nenhum dos objetos aos quais você a atribui. Isso muda no segundo exemplo. Você também pode fazer sinon.replace(obj, fieldname, fake)
para conseguir o mesmo. É apenas JavaScript 🙂
Muito justo - obrigado pela explicação.
Na sexta-feira, 19 de fevereiro de 2021 às 7h02, Carl-Erik Kopseng [email protected]
escrevi:
@scottpatrickwright https://github.com/scottpatrickwright Está fechado
porque não há bug, como deve ser evidente a partir do tópico. Era só
erro do usuário, como no seu caso: funciona conforme descrito e pretendido :-)A caixa de areia Sinon pode limpar coisas que conhece. Isso significa que a Sinon tem
ter conhecimento dos objetos com os quais interage. O esboço de você
descrito acima torna-se autônomo. A Sinon não está ciente de nenhum dos
objetos aos quais você atribui. Isso muda no segundo exemplo. Você poderia
também faça sinon.replace (obj, fieldname, fake) para conseguir o mesmo. É apenas
JavaScript 🙂-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/sinonjs/sinon/issues/2065#issuecomment-782032440 ou
Cancelar subscrição
https://github.com/notifications/unsubscribe-auth/AAKKRTFVMGEYVIB5GSIDXKTS7ZHMRANCNFSM4IGBASQQ
.
Comentários muito úteis
Muito justo - obrigado pela explicação.
Na sexta-feira, 19 de fevereiro de 2021 às 7h02, Carl-Erik Kopseng [email protected]
escrevi: