<p>sinon.restore () &amp;&amp; sandbox.restore () não restaurando falsificações</p>

Criado em 23 jul. 2019  ·  16Comentários  ·  Fonte: sinonjs/sinon

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):

  • Versão da biblioteca: Sinon ^ 7.3.2
  • Ambiente: Nó 10.16.0
  • URL de exemplo: N / a
  • Outras bibliotecas que você está usando: Mocha, Chai

Contexto adicional
Apenas testando os recursos do Sinon enquanto o uso para testar um back-end

Bug Help wanted

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:

@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
.

Todos 16 comentários

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:

image

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

  1. Remove todas as falsificações da caixa de areia
  2. Em seguida, passa pela coleção de falsificações na caixa de areia (agora vazia) e tenta redefinir a lista (vazia) de falsificações

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
.

Esta página foi útil?
0 / 5 - 0 avaliações