Como espionar em uma propriedade de valor (em vez de um método) com Jasmine

115

O Jasmine's spyOné bom para mudar o comportamento de um método, mas existe alguma maneira de mudar uma propriedade de valor (ao invés de um método) para um objeto? o código pode ser como abaixo:

spyOn(myObj, 'valueA').andReturn(1);
expect(myObj.valueA).toBe(1);
Shuping
fonte

Respostas:

97

Em fevereiro de 2017, eles mesclaram um PR adicionando esse recurso, lançado em abril de 2017.

então, para espionar getters / setters que você usa: const spy = spyOnProperty(myObj, 'myGetterName', 'get'); onde myObj é sua instância, 'myGetterName' é o nome daquele definido em sua classe como get myGetterName() {}e o terceiro parâmetro é o tipo getou set.

Você pode usar as mesmas afirmações que já usa com os espiões criados com spyOn.

Então você pode, por exemplo:

const spy = spyOnProperty(myObj, 'myGetterName', 'get'); // to stub and return nothing. Just spy and stub.
const spy = spyOnProperty(myObj, 'myGetterName', 'get').and.returnValue(1); // to stub and return 1 or any value as needed.
const spy = spyOnProperty(myObj, 'myGetterName', 'get').and.callThrough(); // Call the real thing.

Esta é a linha no código-fonte do github onde esse método está disponível, se você estiver interessado.

https://github.com/jasmine/jasmine/blob/7f8f2b5e7a7af70d7f6b629331eb6fe0a7cb9279/src/core/requireInterface.js#L199

Respondendo à pergunta original, com o jasmine 2.6.1, você:

const spy = spyOnProperty(myObj, 'valueA', 'get').andReturn(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();
Juan
fonte
7
Como faço isso se valueAfor um Observableou Subject? Estou recebendoProperty valueA does not have access type get
Ka Mok
4
Isso provavelmente significa que você não pode usá-lo nessa propriedade. spyOnProperty está usando getOwnPropertyDescriptor e verificando se o tipo de acesso get | set para esse descritor de propriedade existe. O erro que você está recebendo é porque o descritor de propriedade não foi definido ou obtido para a propriedade que você está tentando espionar. Jasmine faz isso: const descriptor = Object.getOwnPropertyDescriptor ... if (! Descriptor [accessType]) {// o erro é gerado}
Juan
Portanto, não há como espionar uma propriedade sem os métodos getter e setter explícitos? Espiar é uma propriedade usada.
Dominik
@Dominik Aqui eu escrevi todas as coisas subjacentes
Juan
1
@Mathieu não é uma boa afirmação porque apenas afirma o que já sabemos do que dissemos ao espião para fazer, mas é apenas o que eles estavam perguntando e seu mesmo trecho de código de pergunta ¯_ (ツ) _ / ¯ O ponto de sua questão era como espionar uma propriedade e não tanto seu exemplo específico.
Juan
12

Alguma razão pela qual você não pode simplesmente alterá-lo no objeto diretamente? Não é como se o javascript reforçasse a visibilidade de uma propriedade em um objeto.

Paul Harris
fonte
1
usar spyOnexplicitamente indica que quero simular algo, enquanto defino diretamente a propriedade indica implicitamente que quero simular algo e não tenho certeza se alguém entenderá que estou zombando de algo quando estiver lendo o código. Outro caso é que eu não quero mudar o comportamento interno do objeto, por exemplo, se eu mudar a propriedade de comprimento de um array, o array é aparado, então uma simulação será melhor
Shuping
@Shuping, neste caso, você não estaria zombando. Você estaria fazendo stub, o que é perfeitamente normal. Você estaria "mudando o comportamento" apenas dentro do teste, que é o que você estava tentando alcançar com o spyOn.
Fabio Milheiro
Possivelmente, você deseja espionar o protótipo do objeto de tempo de execução para ter certeza de que a propriedade existirá no tempo de execução. Jasmine spyOnfalha no teste se a propriedade não existir.
sennett,
2
Um exemplo é definir ou excluir window.sessionStorage:TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>'
Chris Sattinger
2
Nem sempre é possível reatribuir uma propriedade de objeto em javascript
Renaud
12

Jasmine não tem essa funcionalidade, mas você pode conseguir hackear algo junto usando Object.defineProperty.

Você pode refatorar seu código para usar uma função getter e, em seguida, espionar o getter.

spyOn(myObj, 'getValueA').andReturn(1);
expect(myObj.getValueA()).toBe(1);
Eric
fonte
Sim, é o que posso fazer por enquanto.
Shuping
4
para jasmim 2 é:and.returnValue(1)
jtzero
9

A melhor forma é usar spyOnProperty. Ele espera 3 parâmetros e você precisa passar getou setcomo um terceiro parâmetro.

Exemplo

const div = fixture.debugElement.query(By.css('.ellipsis-overflow'));
// now mock properties
spyOnProperty(div.nativeElement, 'clientWidth', 'get').and.returnValue(1400);
spyOnProperty(div.nativeElement, 'scrollWidth', 'get').and.returnValue(2400);

Aqui eu estou definindo o getde clientWidthde div.nativeElementobjeto.

Aniruddha Das
fonte
6

Se você estiver usando ES6 (Babel) ou TypeScript, você pode remover a propriedade usando os acessadores get e set

export class SomeClassStub {
  getValueA = jasmine.createSpy('getValueA');
  setValueA = jasmine.createSpy('setValueA');
  get valueA() { return this.getValueA(); }
  set valueA(value) { this.setValueA(value); }
}

Então, em seu teste, você pode verificar se a propriedade está definida com:

stub.valueA = 'foo';

expect(stub.setValueA).toHaveBeenCalledWith('foo');
Martin
fonte
Ou, se os getters fizerem parte da classe em teste, os stubs podem ser injetados em uma subclasse.
ccprog
6

A maneira certa de fazer isso é com a propriedade do espião, que permitirá simular uma propriedade em um objeto com um valor específico.

const spy = spyOnProperty(myObj, 'valueA').and.returnValue(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();
alexalejandroem
fonte
1

Suponha que haja um método como este que precisa de teste A srcpropriedade da imagem minúscula precisa de verificação

function reportABCEvent(cat, type, val) {
                var i1 = new Image(1, 1);
                var link = getABC('creosote');
                    link += "&category=" + String(cat);
                    link += "&event_type=" + String(type);
                    link += "&event_value=" + String(val);
                    i1.src = link;
                }

O spyOn () abaixo faz com que a "nova imagem" seja alimentada com o código falso do teste o código spyOn retorna um objeto que tem apenas uma propriedade src

Como a variável "gancho" tem o escopo definido para ser visível no código falso no SpyOn e também mais tarde após o "reportABCEvent" ser chamado

describe("Alphabetic.ads", function() {
    it("ABC events create an image request", function() {
    var hook={};
    spyOn(window, 'Image').andCallFake( function(x,y) {
          hook={ src: {} }
          return hook;
      }
      );
      reportABCEvent('testa', 'testb', 'testc');
      expect(hook.src).
      toEqual('[zubzub]&arg1=testa&arg2=testb&event_value=testc');
    });

Isso é para o jasmine 1.3, mas pode funcionar no 2.0 se "andCallFake" for alterado para o nome 2.0

Vorsprung
fonte
1

Estou usando uma grade de kendo e, portanto, não posso alterar a implementação para um método getter, mas quero testar em torno disso (simulando a grade) e não testar a grade em si. Eu estava usando um objeto espião, mas ele não suporta simulação de propriedade, então faço o seguinte:

    this.$scope.ticketsGrid = { 
        showColumn: jasmine.createSpy('showColumn'),
        hideColumn: jasmine.createSpy('hideColumn'),
        select: jasmine.createSpy('select'),
        dataItem: jasmine.createSpy('dataItem'),
        _data: []
    } 

É um pouco prolixo, mas funciona muito bem

jolySoft
fonte
0

Estou um pouco atrasado para a festa aqui eu sei, mas,

Você pode acessar diretamente o objeto de chamadas, que pode fornecer as variáveis ​​para cada chamada

expect(spy.calls.argsFor(0)[0].value).toBe(expectedValue)
CosmicChild
fonte
-3

Você não pode simular uma variável, mas pode criar uma função getter para ela e simular esse método em seu arquivo de especificação.

Gampesh
fonte