<p>sinon.restore () &amp;&amp; sandbox.restore () не восстанавливает фейки</p>

Созданный на 23 июл. 2019  ·  16Комментарии  ·  Источник: sinonjs/sinon

Опишите ошибку
Не удается очистить поддельные 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 ^ 7.3.2
  • Среда: узел 10.16.0
  • Пример URL: н / д
  • Другие библиотеки, которые вы используете: Mocha, Chai

Дополнительный контекст
Просто тестирую возможности Sinon, поскольку я использую его для тестирования серверной части.

Bug Help wanted

Самый полезный комментарий

Достаточно честно - спасибо за объяснение.

Пт, 19 февраля 2021 г., в 7:02 Карл-Эрик Копсенг [email protected]
написал:

@scottpatrickwright https://github.com/scottpatrickwright Закрыто
потому что ошибки нет, как должно быть видно из потока. Это было просто
ошибка пользователя, как в вашем случае: он работает как описано и предназначено :-)

Песочница Sinon может очистить то, о чем она знает. Это означает, что у Синон
знать объекты, с которыми он взаимодействует. Заглушка вы
описанное выше сделано автономно. Синон не осведомлена ни о каких
объекты, которым вы его назначаете. Это изменится во втором примере. Вы могли
также выполните sinon.replace (obj, fieldname, fake), чтобы добиться того же. Это просто
JavaScript 🙂

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/sinonjs/sinon/issues/2065#issuecomment-782032440 или
отказаться от подписки
https://github.com/notifications/unsubscribe-auth/AAKKRTFVMGEYVIB5GSIDXKTS7ZHMRANCNFSM4IGBASQQ
.

Все 16 Комментарий

Как бы то ни было, это похоже на ошибку, но, честно говоря, я не совсем уверен в этом. Я помню, что ключевым моментом для подделок было то, что они должны были быть в некотором смысле неизменяемыми, но поскольку у них действительно есть состояние (т.е. они знают, были ли они вызваны), я полагаю, что неизменность должна быть ограничена только поведением заглушки.

@mroderick может быть подходящим человеком, чтобы ответить на этот вопрос ...

Если это правильное поведение, мы должны хотя бы обновить документы.

@ fatso83 Очень признателен! Документ .restore упомянул подделки:

image

отсюда .

Я только что столкнулся с этой ошибкой, так что было бы неплохо ее исправить.

Если в ближайшие несколько дней никто не сделает пиара, я посмотрю, смогу ли я найти время, чтобы исправить это.

Я изучил это сейчас, и документы немного вводят в заблуждение, как и тест выше. На самом деле кажется, что он делает (почти) то, что говорит, но давайте сначала определим некоторые термины:

Восстановление - восстановление песочницы означает восстановление всех заглушенных функций в их исходное состояние, то есть заглушки удаляются из каждого объекта, если они перезаписали исходный.
reset - сброс означает, что состояние каждой из созданных фейков очищено, но они все еще присутствуют на объектах.

Проблема с вышеуказанным тестом в том, что он

  1. Удаляет все фейки из песочницы
  2. Затем переходит к просмотру коллекции фейков в песочнице (теперь пустой) и пытается сбросить (пустой) список фейков.

Это не имеет смысла :-)

Я проверил, и вызовы 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

Пять минут назад я изменил его на:

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.replace(obj, fieldname, fake) чтобы добиться того же. Это просто JavaScript 🙂

Достаточно честно - спасибо за объяснение.

Пт, 19 февраля 2021 г., в 7:02 Карл-Эрик Копсенг [email protected]
написал:

@scottpatrickwright https://github.com/scottpatrickwright Закрыто
потому что ошибки нет, как должно быть видно из потока. Это было просто
ошибка пользователя, как в вашем случае: он работает как описано и предназначено :-)

Песочница Sinon может очистить то, о чем она знает. Это означает, что у Синон
знать объекты, с которыми он взаимодействует. Заглушка вы
описанное выше сделано автономно. Синон не осведомлена ни о каких
объекты, которым вы его назначаете. Это изменится во втором примере. Вы могли
также выполните sinon.replace (obj, fieldname, fake), чтобы добиться того же. Это просто
JavaScript 🙂

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/sinonjs/sinon/issues/2065#issuecomment-782032440 или
отказаться от подписки
https://github.com/notifications/unsubscribe-auth/AAKKRTFVMGEYVIB5GSIDXKTS7ZHMRANCNFSM4IGBASQQ
.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги