jasmine.createSpyObj com propriedades
Ao simular dependências em meus testes Angular, geralmente crio um objeto espião usando jasmine.createSpyObj:
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
em seguida, forneça-o ao TestBed:
providers: [
{provide: MyService, useValue: serviceSpy}
]
Ao usá-lo em meu teste, posso especificar o valor de retorno desejado:
serviceSpy.method.and.returnValue(of([...]));
Agora também preciso simular propriedades e não consigo descobrir como isso deve ser feito. createSpyObjpermite a definição de nomes de propriedades:
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], ['property']);
mas tentei várias soluções com base nos inúmeros artigos e respostas por aí sem sucesso, por exemplo:
// Cannot read property 'and' of undefined
serviceSpy.property.and.returnValue(true);
// not declared configurable
spyOnProperty(serviceSpy, 'property').and.returnValue(true);
// no build errors, but value stays 'undefined'
serviceSpy.property = true;
A única maneira de fazer 'meio' funcionar é:
let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});
O problema aqui é que é um conjunto único na criação. Se eu quiser alterar o valor esperado no teste, não funciona.
fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';
Existe uma solução para os métodos e propriedades fictícios, criando um objeto espião, ou devo criar minha própria classe falsa na qual posso usar spyOne spyOnProperty?
Eu também gostaria de saber qual é o uso da matriz de propriedades na createSpyObjdefinição. Até agora não vi nenhum exemplo na web que explique isso.
Respostas
Pela documentação (grifo meu):
Você pode criar um objeto espião com várias propriedades rapidamente passando um array ou hash de propriedades como um terceiro argumento para
createSpyObj. Neste caso, você não terá uma referência aos espiões criados, então se você precisar alterar suas estratégias de espionagem mais tarde, você terá que usar aObject.getOwnPropertyDescriptorabordagem .it("creates a spy object with properties", function() { let obj = createSpyObj("myObject", {}, { x: 3, y: 4 }); expect(obj.x).toEqual(3); Object.getOwnPropertyDescriptor(obj, "x").get.and.returnValue(7); expect(obj.x).toEqual(7); });
As propriedades espiadas são descritores (veja, por exemplo, Object.definePropertyno MDN ), portanto, para acessar os objetos espiões, você precisa obter o objeto descritor e interagir com os métodos gete setdefinidos nele.
No TypeScript, o compilador precisa de um pouco de ajuda. createSpyObjretorna anyou SpyObj<T>, e SpyObjapenas define os métodos como espionados:
type SpyObj<T> = T & {
[K in keyof T]: T[K] extends Func ? T[K] & Spy<T[K]> : T[K];
// | if it's a | spy on it | otherwise leave
// | callable | | it alone
};
Portanto, para acessar .ando getter do descritor, você precisará de encadeamento opcional (como Object.getOwnPropertyDescriptorpode retornar undefined) e uma declaração de tipo para um Spy:
(Object.getOwnPropertyDescriptor(obj, "x")?.get as Spy<() => number>).and.returnValue(7);
Parque infantil
Muito obrigado @jonrsharpe! Acabei de adicionar uma função para fazer:
spyPropertyGetter(spy, 'propName').and.returnValue(...)
Que é definido como:
function spyPropertyGetter(
spyObj: jasmine.SpyObj<any>,
propName: string
) {
return Object.getOwnPropertyDescriptor(spyObj, propName).get as jasmine.Spy<
jasmine.Func
>;
}