Sinon: Sandbox lança erro 'não é possível criar stub de propriedade própria inexistente'

Criado em 18 ago. 2017  ·  14Comentários  ·  Fonte: sinonjs/sinon

Acabei de tentar atualizar do 2.4.1 para o 3.2.1 e encontrei o seguinte problema. Este código funciona em 2.4.1:

        const spy = sandbox.spy();
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: spy
            }
        });

Mas no 3.2.1 ele lança uma exceção: TypeError: Cannot stub non-existent own property google

Não é mencionado no guia de migração, por isso parece ser uma regressão.

Bug Regression

Comentários muito úteis

~ Obrigado por restaurar este comportamento. ~

Desejava adicionar um caso de uso que suporta stubbing de propriedades inexistentes.

No meu caso de uso, estou criando um esboço de uma propriedade em um objeto de configuração. O objeto config tem várias chaves opcionais e é inicializado carregando um arquivo da máquina do desenvolvedor. Quando executo um teste específico, preciso definir uma dessas chaves com um valor conhecido e, em seguida, restaurar o objeto do desenvolvedor como estava.

sandbox.stub(serverSecrets, 'the_key_i_need_set').value(fakeValue) é uma maneira muito clara de transmitir isso. É bom obter o mesmo comportamento, _embora não saiba em tempo de execução se a chave está definida ou não_.

Todos 14 comentários

Relacionado a # 1512.

Se a propriedade não existir, você não precisa adicioná-la à sandbox. Basta sobrescrever. Mas sim, se funcionou antes, e não dissemos explicitamente que deveria mudar, então é uma regressão.

Não tenho certeza do que devemos fazer aqui. Atualizar os documentos para dizer que o stub de valores não existentes não faz sentido e não é compatível ou torna isso possível?

Se a propriedade não existir, você não precisa adicioná-la à sandbox. Basta sobrescrever.

O bom de adicionar a propriedade ao sandbox é que o sinon me ajuda a manter meu ambiente de teste global limpo entre cada teste por meio de sandbox.restore() . É um recurso extremamente útil, especialmente ao lidar com bibliotecas de terceiros como o Google Maps, onde eu não controlo a API. Seria ótimo se pudesse funcionar na linha 3.x.

Também acabei de notar que cometi o pecado de não dar um exemplo completo. Meu sandbox está sendo criado com o formato 2.4.1:

let sandbox;

before(() => { sandbox = sinon.sandbox.create(); })
afterEach(() => { sandbox.restore(); })

Não tenho certeza se isso é importante; desculpas por não fornecê-lo antes.

Eu acho que em cenários como o que @ZebraFlesh descreve, eu preferiria que o texto fixo fosse mais explícito.

// not so explicit, doesn't work with [email protected]
beforeEach(function() {
    const spy = sandbox.spy();
    sandbox.stub(window, 'google').value({
        maps: {
            LatLng: x => x,
            Map: spy
        }
    }); 
});
// more explicit, works with sinon<strong i="9">@2</strong>, sinon<strong i="10">@3</strong>
function setGoogleMapsFixture(sandbox) {
    window.google = {
        maps: {
            LatLng: x => x,
            Map: sandbox.spy()
        }
    };
}

function removeGoogleMapsFixture() {
    delete window.google;
}

beforeEach(function() {
    setGoogleMapsFixture(sandbox)
});

// not using afterEach, as this only needs to happen
// after the last test in this block is run
after(function() {
    removeGoogleMapsFixture();
});

Com uma configuração mais explícita do acessório, conforme descrito acima, você não precisa de um recurso no Sinon que permitiria o stub de propriedades próprias inexistentes.

Não tenho certeza do que devemos fazer aqui. Atualizar os documentos para dizer que o stub de valores não existentes não faz sentido e não é compatível ou torna isso possível?

Embora eu reconheça que pode ser conveniente em alguns cenários (como o descrito por @ZebraFlesh), acho que criar um esboço de propriedades próprias não existentes provavelmente levará a erros nos testes, nos quais o teste passa porque o autor digitou incorretamente o nome de a propriedade existente que pretendiam arrancar. Devemos ter como objetivo eliminar a possibilidade de erros onde podemos, sem ser muito restritivos.

Acho que o esboço de propriedades próprias não existentes deve permanecer sem suporte. Devemos atualizar a documentação.

@mroderick Concordo com você no sentido de que pode causar menos bugs, mas já oferecemos suporte para stubs normais. Se deixarmos de apoiar esse comportamento, precisaremos removê-lo também, para sermos consistentes. Seria estranho oferecer suporte a esse recurso apenas fora das caixas de proteção, já que as caixas de proteção geralmente _adicionam_ algumas possibilidades. E remover o suporte é um recurso de quebra, portanto, um grande solavanco também seria necessário.

Então:

  • remova a seleção prontamente para sandboxes para corrigir esse recurso de quebra

ou e(?)

  • remover a funcionalidade de stubs normais e em sandbox

    • lançar uma nova versão principal com documentos atualizados

Com uma configuração mais explícita do acessório, conforme descrito acima, você não precisa de um recurso no Sinon que permitiria o stub de propriedades próprias inexistentes.

Acho que funciona bem se sua fixação nunca variar entre os testes. No entanto, minha fixação sim. Um exemplo simples cobre casos de sucesso e falha:

it('handles the success case', () => {
        const spy = sandbox.spy();
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: spy
            }
        });
        // ... test, including asserting that the spy was called
});

it('handles the failure case', () => {
        const msg = 'test error';
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: sandbox.stub().throws(new Error(msg))
            }
        });
        // ... test, ignoring spy calls and instead focusing on error handling
});

O comportamento no 2.x tem a vantagem de tudo ser devidamente limpo após cada teste por meio de sandbox.restore() . Usando o exemplo de configuração de fixação mais explícito descrito acima, suponho que você possa excluir a propriedade não própria em um gancho afterEach para obter o mesmo efeito.

Para resolver o problema de introdução de erros potenciais ao digitar inadvertidamente o nome de uma propriedade própria existente, o sinon pode modificar a API pública:

  • stub.ownValue() : stubs apenas propriedades próprias, lança para propriedades não próprias
  • stub.value() : stubs apenas propriedades não próprias, lança para propriedades próprias

A API torna-se mais explícita e o consumidor é forçado a escolher a ferramenta apropriada para a tarefa em questão.

Isso está muito relacionado à discussão em # 1508 (embora trate de stubs normais) h, onde @lucasfcosta tem a visão oposta - que _não devemos jogar_ para undefined propriedades. Seja o que for que achemos, acredito fortemente que _precisamos ser consistentes_ nas APIs de stub para stubs e sandboxes normais. Não devemos apoiá-lo em um caso, e não no outro.

No momento, a situação é:

  • stubs normais costumavam lançar 1.x, mas isso mudou no 2.0 e não lança mais agora
  • sandboxes não costumavam jogar, mas começaram a jogar 3.1 (?)

Então, por um tempo tivemos paridade de recursos, mas depois a perdemos de novo ... Não acho que esse zigue-zague seja muito benéfico para os usuários, então devemos iniciar esta discussão. Embora eu concorde com Morgan no sentido de que isso pode servir para testes mais específicos, não gosto de descartar um comportamento para duas versões principais e, em seguida, adicioná-lo novamente. Acho que faria menos barulho (correções para clientes, dúvidas / problemas neste rastreador) apenas para reverter essa regressão.

Embora eu entenda o inconveniente, parece que há uma solução fácil com alterações mínimas de código.

before(function() {
  window.google = 'This is a placeholder for sinon to overwrite.';
});

after(function() {
  delete window.google;
});

Isso permite que o código sinon permaneça inalterado.

Regressão vs documentação deficiente

Este parece ser o comportamento esperado, uma vez que existem testes para ele. Devemos atualizar os documentos para refletir isso em minha opinião. Ele veio em uma grande, então mudanças significativas são toleradas.

@fearphage Manter o status quo significa que o stub de campos não existentes é um comportamento não suportado para sandboxes, embora seja um comportamento suportado para stubs normais. Não é um pouco lamentável que os dois conjuntos de recursos não estejam alinhados?

A resolução foi implementada em # 1557

Eu li os vários threads e posso ver por que isso aconteceu, mas é uma verdadeira dor no Typescript, onde você geralmente tem funções que são implementadas em um protótipo de classe, caso em que o sinon cospe o manequim mesmo que tudo pareça bem de tipo sábio (uma vez que keyof YourType permitirá alegremente todas as funções públicas que são definidas mais abaixo na cadeia de protótipos).

Eu entendo que o Typescript provavelmente não é uma prioridade para vocês, mas mesmo em JS parece contra intuitivo que myObject.callMe() será executado perfeitamente bem, enquanto sinon.stub(myObject, "callMe") não será nesse caso. Eu prefiro não ter que investigar como aquele objeto específico foi montado apenas para saber como fazer o stub.

Eu realmente acho que este é um caso de uso importante para o qual fazer um caminho feliz, considerando que as classes estão recebendo mais suporte nativo em JS.

Se você receber um erro dizendo que o método é indefinido no objeto, você sabe que o erro provavelmente está no protótipo. Em seguida, modificar diretamente o objeto usando myObject.callMe = sinon.stub(); não parece ser muito incômodo, IMHO ... Também deve evitar a criação de funções de limpeza / desmontagem, já que o protótipo nunca foi alterado.

Sim, eu acho que não é muito difícil de contornar, é apenas colocar mais carga cognitiva em mim estar ciente de como as coisas são implementadas.

Também parece inesperado, de modo que senti a necessidade de adicionar um comentário no teste para explicar por que o código de stub em 2 linhas consecutivas para o mesmo objeto era diferente e por que eu estava excluindo manualmente um dos stubs em minha desmontagem, mas o outro foi tratado pela caixa de areia.

~ Obrigado por restaurar este comportamento. ~

Desejava adicionar um caso de uso que suporta stubbing de propriedades inexistentes.

No meu caso de uso, estou criando um esboço de uma propriedade em um objeto de configuração. O objeto config tem várias chaves opcionais e é inicializado carregando um arquivo da máquina do desenvolvedor. Quando executo um teste específico, preciso definir uma dessas chaves com um valor conhecido e, em seguida, restaurar o objeto do desenvolvedor como estava.

sandbox.stub(serverSecrets, 'the_key_i_need_set').value(fakeValue) é uma maneira muito clara de transmitir isso. É bom obter o mesmo comportamento, _embora não saiba em tempo de execução se a chave está definida ou não_.

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

Questões relacionadas

brettz9 picture brettz9  ·  3Comentários

fearphage picture fearphage  ·  4Comentários

zimtsui picture zimtsui  ·  3Comentários

akdor1154 picture akdor1154  ·  4Comentários

sudhirbits picture sudhirbits  ·  4Comentários