Uma ferramenta poderosa para simplificar seus testes angulares
O Spectator ajuda você a se livrar de todo o trabalho grunhido de caldeira, deixando você com testes de unidade legíveis, elegantes e simplificados.
✅ Suporte para testar componentes, diretrizes e serviços angulares
✅ Fácil DOM consulta
✅ API limpa para desencadear eventos de teclado/mouse/toque
✅ Testando ng-content
✅ Matchers personalizados de jasmim/jest (ToHaveclass, Tabedisabled ..)
✅ Suporte de teste de roteamento
✅ Suporte de teste HTTP
✅ Suporte interno para componentes de entrada
✅ Suporte interno para fornecedores de componentes
✅ Fornecedores de auto-batedura
✅ digitado fortemente
✅ Suporte de brincadeira
Os patrocínios auxiliam no desenvolvimento e manutenção contínua das bibliotecas NGNEAT. Considere pedir à sua empresa que patrocine a NGNEAT como seu núcleo para o desenvolvimento de negócios e aplicativos.
Eleve seu apoio, tornando -se um patrocinador de ouro e tenha seu logotipo destacado em nosso ReadMe nos 5 principais repositórios.
Aumente seu apoio ao se tornar um patrocinador de ouro e aproveitar os holofotes com seu logotipo com destaque nos três principais repositórios do nosso ReadMe.

Torne -se um patrocinador de bronze e obtenha seu logotipo em nosso ReadMe no Github.
Características
Índice
Instalação
Npm
Fio
Componentes de teste
Vistas diferidas aninhadas
Seletor de string
Seletor de tipo
Seletor DOM
Testando elementos de seleção
Componentes zombeteiros
Testando módulos angulares de componente único/diretiva
Eventos personalizados
Criadores de eventos
API de eventos
Ajudantes do teclado
Ajudantes de mouse
Perguntas
Visualizações diferidas
Teste com host
Componente de host personalizado
Teste com roteamento
Desencadeando uma navegação
Teste de integração com RouterTestingModule
Opções de roteamento
Diretivas de teste
Serviços de teste
Opções adicionais
Testando tubos
Usando o componente host personalizado
Provedores de zombaria
Zombando de dependências oninit
Zombando dependências do construtor
Suporte de brincadeira
Teste com HTTP
Injeções globais
Provedores de componentes
Matcores personalizados
Esquemas
Coleção de esquemas padrão
Spectador de trabalho e recompra de amostra de JEST e comparação de karma
Equipe Core
Colaboradores
npm install @ngneat/spectator --save-dev
yarn add @ngneat/spectator --dev
Crie uma fábrica de componentes usando a função createComponentFactory() , passando a classe de componente que você deseja testar. O createComponentFactory() retorna uma função que criará um novo componente em cada bloco it :
importar {espectador, createComponentFactory} de '@ngneat/spectator'; importar {buttonComponent} de './button.comPONNE';
Deixe o espectador: espectador <buttonComponent>;
const CreateComponent = CreateComponentFactory (ButtonComponent);
antes e cada vez (() => espectador = createComponent ());
ele ('deve ter uma classe de sucesso por padrão', () => {expect (spectator.query ('button')). ToHaveclass ('sucesso');
});
ele ('deve definir o nome da classe de acordo com a entrada [ClassName]', () => {spectator.setInput ('ClassName', 'Danger'); espera (Spectator.Query ('Button')). ToHaveclass (' perigo '); espera (espectador.Query (' Button ')). Not.tohaveclass (' sucesso ');
});}); A função createComponentFactory pode opcionalmente tomar as seguintes opções que estendem as opções básicas do módulo de teste angular:
const CreateComponent = CreateComponentFactory ({
Componente: ButtonComponent,
importações: [],
Provedores: [],
declarações: [],
EntryComponents: [],
ComponentProviders: [], // substitui os provedores do componente
ComponentViewProviders: [], // substitui os provedores de visualização do componente
Overidemodules: [], // Substituir módulos
excedentesComponentes: [], // Substituir componentes no caso de testar o componente independente
Evertectives: [], // Substituir as diretivas no caso de testar a diretiva independente
Substituirpipes: [], // Substituir tubos no caso de testar tubo independente
zombares: [], // provedores que serão ridicularizados automaticamente
ComponentMocks: [], // fornecedores de componentes que serão ridicularizados automaticamente
ComponentViewProvidersMocks: [], // Componente Vista provedores que serão ridicularizados automaticamente
DetectChanges: false, // Padrões para True
Declarecomponent: False, // Padrões para True
Desabreados: Falso, // Padrões para True
raso: verdadeiro, // padrão para false
adiamentoBlockBeHavior: adiFlockBehavior // Padrões para adierblockBehavior.playthrough}); A função createComponent() opcionalmente leva as seguintes opções:
it ('deveria ...', () => {
espectador = createComponent ({// o componente inputSProps: {title: 'click'}, // substitui os provedores do componente // observe que você deve declará -lo uma vez em `createComponentFactory`providers: [], // se deve executar a detecção de alteração (Padrões para True) DetectChanges: false
});
Espere (Spectator.Query ('Button')). ToHaveText ('Click');}); Ao fornecer opções overrideComponents em escopo de nossa função createComponent() podemos definir a maneira de substituir o componente independente e suas dependências
@Componente({
Seletor: `App-Standalone-With-Import`,
Modelo: `<div id =" independente "> componente independente com importação!
importações: [StandalOneComponentWithDependência],
Standalone: true,}) Exportar classe StandaloneWithimportSComponent {} @component ({
Seletor: `App-Standalone-With-Dependency`,
Modelo: `<div id =" StandaloneWithDependência "> Componente independente com dependência! </div>`,
Standalona: True,}) Classe de exportação StandalOneComponentWithDependency {
Construtor (consulta pública: QueryService) {}}@componente ({
Seletor: `App-Standalone-With-Dependency`,
Modelo: `<div id =" StandaloneWithDependência "> Componente independente com dependência de substituição! </div>`,
Standalone: True,}) Classe exporta
construtor () {}} it ('deve ...', () => {
const Spectator = CreateHostFactory ({Componente: StandaloneWithimportSComponent, Modelo: `<div> <pp-standalone-with-import> </app-standalone-with-import> </div>`, exagercomponentes: [[StandalOneWithimportscomcomponente, Remova: {importações: [StandaloneComponentWithDependency]}, Adicione: {Imports: [MockStandalOneComponentWithDependency]},},],],
});
Espere (host.Query ('#Standalone')). ToContaintext ('Componente independente com importação!');
Espere (host.Query ('#StandaloneWithDependency')). O método createComponent() retorna uma instância de Spectator que expõe a seguinte API:
fixture - o acessório do componente testado
component - a instância do componente testado
element - o elemento nativo do componente testado
debugElement - o elemento de depuração do acessório testado
flushEffects() - fornece um invólucro para TestBed.flushEffects()
inject() - fornece um invólucro para TestBed.inject() :
const Service = Spectator.Inject (QueryService); const deComponentInjector = true; const Service = Spectator.Inject (QueryService, FromComponentInjector);
detectChanges() - executa DetectChanges no elemento testado/host:
espectador.DetectChanges ();
detectComponentChanges() - Executa detectChanges no componente testado (não no host ). Você precisará desse método em casos raros ao usar um host e o componente testado é onPush , e deseja forçá -lo a executar um ciclo de detecção de alterações.
espectador.DetectComponentChanges ();
setInput() - altera o valor de um @Input () do componente testado. O método é executado ngOnChanges com SimpleChanges manualmente, se houver.
it ('deveria ...', () => {
espectador.setInput ('ClassName', 'Danger');
Spectator.SetInput ({ClassName: 'Danger'
});}); output - Retorna um observável @Output () do componente testado:
ele ('deve emitir o $ Evento em clique', () => {
Deixe a saída;
espectador.Output ('Click'). Inscreva -se (resultado => (output = resultado));
spectator.component.OnClick ({type: 'Click'});
Espere (saída) .ToEqual ({type: 'Click'});}); tick(millis?: number) - Execute a função fakeasync tick() e chama detectChanges() :
It ('deve funcionar com tick', fakeasync (() => {
espectador = createComponent (ZippyComponent);
spectator.component.update ();
espera (spectator.component.UpDatedAsync) .ToBefalsy ();
espectador.Tick (6000);
Espere (Spectator.component.UpDatedasync) .Not.tobefalsy ();}))) Cada um dos eventos pode aceitar um SpectatorElement que pode ser um dos seguintes:
Tipo SpectatorElement = String | Elemento | DebugElement | ElementRef | Janela | Documento | Domselector;
Se não for fornecido, o elemento padrão será o elemento host do componente em teste.
click() - desencadeia um evento de clique:
Spectator.Click (SpectatorElement); Spectator.Click (BYTEXT ('Element')); blur() - desencadeia um evento Blur:
Spectator.blur (SpectatorElement); Spectator.blur (BYTEXT ('elemento'));Observe que, se estiver usando a estrutura do jest, o Blur () funciona apenas se o elemento estiver focado. Detalhes.
focus() - desencadeia um evento de foco:
Spectator.focus (SpectatorElement); Spectator.focus (BYTEXT ('elemento')); typeInElement() - simulando a digitação do usuário:
Spectator.Typeinelement (Valor, SpectatorElement); Spectator.Typeinelement (Valor, Bytext ('Element')); dispatchMouseEvent() - desencadeia um evento de mouse:
Spectator.dispatchMouseEvent (SpectatorElement, 'MouseOut'); Spectator.DispatchMouseEvent (SpectatorElement, 'MouseOut'), X, Y, Event); Spectator.DispatchMouSeEvent (Bytext ('Element', 'MouseOut'); ('Element'), 'mouseout', x, y, evento); dispatchKeyboardEvent() - aciona um evento de teclado:
Spectator.dispatchkeyboardEvent (SpectatorElement, 'KeyUp', 'Escape'); Spectator.DispatchKeyboardEvent (SpectatorElement, 'KeyUp', {Key: 'Escape', Keycode: 27}) Spectator.dispKeyboardEvent (Bytext ('Element'), 'KeyUp ',' Escape '); Spectator.DispatchKeyBoardEvent (BYTEXT (' Element '),' KeyUp ', {Key:' Escape ', KeyCode: 27}) dispatchTouchEvent() - desencadeia um evento de toque:
Spectator.dispatchTouchevent (SpectatorElement, Type, X, Y); Spectator.DispatchTouchEvent (BYTEXT ('Element'), Type, X, Y);Você pode desencadear eventos personalizados (@Output () de componentes filhos) usando o seguinte método:
spectator.triggerEventHandler(MyChildComponent, 'myCustomEvent', 'eventValue');spectator.triggerEventHandler(MyChildComponent, 'myCustomEvent', 'eventValue', { root: true});spectator.triggerEventHandler('app-child-component', 'myCustomEvent ',' EventValue '); Spectator.TriggereventHandler (' App-Child-Component ',' MyCustomevent ',' EventValue ', {root: true});Caso você queira testar os eventos independentemente de qualquer modelo (por exemplo, em serviços de apresentador), você pode fazer falta dos criadores de eventos subjacentes. Eles estão basicamente fornecendo a mesma assinatura sem o elemento anterior.
const teclado
spectator.keyboard.pressenter (); spectator.keyboard.pressescape (); spectator.keyboard.presstab (); spectator.keyboard.pressbackspace (); spectator.keyboard.presskey ('a'); spectator.keyboard.presskey (' ctrl.a '); spectator.keyboard.presskey (' ctrl.shift.a '); Spectator.Mouse.ContextMenu ('. Selector'); Spectator.mouse.dblClick ('. Selector'); Observe que cada um dos métodos acima também será executado detectChanges() .
A API do espectador inclui métodos convenientes para consultar o DOM como parte de um teste: query , queryAll , queryLast , queryHost e queryHostAll . Todos os métodos de consulta são polimórficos e permitem consultar usando qualquer uma das seguintes técnicas.
Passe um seletor de string (no mesmo estilo que faria ao usar jQuery ou document.QuerySelector) para consultar elementos que correspondem a esse caminho no DOM. Este método para consulta é equivalente ao predicado do Angular por.css. Observe que os elementos HTML nativos serão retornados. Por exemplo:
// retorna um único htmlelementspectator.Query ('div> ul.nav li: First-Child'); // Retorna uma matriz de todos os htmlements específicos correspondentes.Queryall ('div> ul.nav li'); Document Contextspectator.Query ('div', {root: true}); spectator.query ('app-child', {leia: ChildServiceService}); Passe um tipo (como um componente, diretiva ou classe de provedor) para consultar as instâncias desse tipo no DOM. Isso é equivalente ao predicado By.directive . Opcionalmente, você pode passar em um segundo parâmetro para ler um token de injeção específico dos injetores dos elementos correspondentes. Por exemplo:
// retorna uma única instância do MyComponent (se presente) espectador.Query (myComponent); // retorna a instância de `SomeService` encontrada na instância de 'MyComponent` que existe no DOM (se presente) espectador.Query (MyComponent , {leia: SomEService}); Spectator.Query (MyComponent, {read: elementRef}); host.QueryLast (ChildComponent); host.Queryall (ChildComponent);O espectador permite consultar elementos usando seletores inspirados na biblioteca DOM-testing. Os seletores disponíveis são:
Spectator.Query (ByPlaceHolder ('Por favor, digite seu endereço de e -mail')); Spectator.Query (ByValue ('por value'))); Spectator.Query (Bytitle ('por Title')); text alt ')); espectador.Query (bylabel (' por etiqueta ')); espectador.Query (bytext (' por text ')); spectator.Query (bytext (' por texto ', {Selector:' #some. seletor '})); Spectator.Query (BYTEXTCOntent (' por conteúdo de texto ', {Selector:' #some .Selector '})); Spectator.Query (ByRole (' Caixa de seleção ', {verificado: true})); A diferença entre byText e byTextContent é que o primeiro não corresponde ao texto dentro de um elementos aninhados.
Por exemplo, neste seguinte html byText('foobar', {selector: 'div'}) não corresponde à seguinte div , mas byTextContent irá:
<div> <span> foo </span> <rpa> bar </span> </div>
O espectador permite consultar elementos aninhados dentro de um elemento pai. Isso é útil quando você tem várias instâncias do mesmo componente na página e deseja consultar as crianças em uma específica. O seletor de pais é um seletor de string usado para encontrar o elemento pai. O seletor de pais é passado como o segundo parâmetro dos métodos de consulta. Por exemplo:
Spectator.Query (ChildComponent, {ParentSelector: '#Parent-Component-1'}); Spectator.Queryall (ChildComponent, {ParentSelector: '#Parent-Component-1'}); O Spectator permite que você teste elementos <select></select> e suporta multi select.
Exemplo:
ele ('deve definir as opções corretas no Multi Select', () => {
const select = spectator.Query ('#test-multi-select') como htmlselectElement;
espectador.SelectOption (selecione, ['1', '2']);
Espere (selecione) .ToHaveSelectedOptions (['1', '2']);}); ele ('deve definir a opção correta no padrão selecionar', () => {
const select = spectator.Query ('#test-single-select') como htmlleclectElement;
espectador.SelectOption (selecione, '1');
Espere (selecione) .ToHaveSelectedOptions ('1');}); Ele também permite verificar se o seu manipulador de eventos change está agindo corretamente para cada item selecionado. Você pode desativar isso se precisar definir as opções pré -despachar o evento de mudança.
API:
Spectator.SelectOption (SelectElement: HTMLElectElement, Opções: String | String [] | HtmloptionElement | htmloptionElement [], config: {emitevents: boolean} = {emitevents: true});Exemplo:
ele ('deve despachar o número correto de eventos de mudança', () => {
const Onchangespy = Spyon (Spectator.component, 'HandLechange');
const select = spectator.Query ('#test-onChange-select') como htmlleclectElement;
espectador.SelectOption (selecione, ['1', '2'], {emitevents: true});
Espere (selecione) .ToHaveSelectedOptions (['1', '2']);
Espere (onchangespy) .toavebeencalledTimes (2);}); it ('não deve despachar o número correto de eventos de mudança', () => {
const Onchangespy = Spyon (Spectator.component, 'HandLechange');
const select = spectator.Query ('#test-onChange-select') como htmlleclectElement;
espectador.SelectOption (selecione, ['1', '2'], {emitevents: false});
Espere (selecione) .ToHaveSelectedOptions (['1', '2']);
Espere (onchangespy) .Not.ToHaveBeEncalledTimes (2);}); Você também pode aprovar o (s )Elemento (s) HTMLOptionElement (s) como argumentos para selectOption e o Matcher toHaveSelectedOptions . Isso é particularmente útil quando você está usando a ligação [ngValue] na <option> :
ele ('deve definir a opção correta em Single Select ao passar o elemento', () => {
const select = spectator.Query ('#teste-seleto-elemento') como htmlselectElement;
espectador.SelectOption (selecione, espectador.Query (bytext ('dois')) como htmloptionElement);
Espere (selecione) .ToHaveSelectedOptions (Spectator.Query (BYTEXT ('TODOS')) como htmloptionElement);}); Se você precisar zombar dos componentes, poderá usar a biblioteca NG-Mocks. Em vez de usar CUSTOM_ELEMENTS_SCHEMA , que pode ocultar alguns problemas e não ajudará a definir entradas, saídas etc., ng-mocks zombam automaticamente das entradas, saídas etc. para você.
Exemplo:
importar {createHostFactory} de '@ngneat/spectator'; importar {mockComponent} de 'ng-mocks'; importar {foocomponent} de './path/to/foo.component' ;const createHost = createHostFactory ({{{
Componente: YourComponentToTest,
Declarações: [MockComponent (foocomponent)
]});Os componentes (ou diretrizes) declarados em seu próprio módulo podem ser testados definindo o módulo de componentes na lista de importações da fábrica de componentes juntamente com o componente. Por exemplo:
const CreateComponent = CreateComponentFactory ({
Componente: ButtonComponent,
importações: [ButtonComponentModule],}); Quando usado assim, no entanto, o espectador adiciona internamente o componente ButtonComponent às declarações do novo módulo criado internamente. Portanto, você verá o seguinte erro:
Type ButtonComponent is part of the declarations of 2 modules [...]
É possível dizer ao espectador para não adicionar o componente às declarações do módulo interno e, em vez disso, usar o módulo explicitamente definido como está. Basta definir a propriedade declareComponent das opções de fábrica como false :
const CreateComponent = CreateComponentFactory ({
Componente: ButtonComponent,
importações: [ButtonComponentModule],
Declarecomponent: false,}); Ao usar o conjunto de fábricas Createtection, a propriedade declareDirective das opções de fábrica para false :
const Creativeirection = CreateRectionFactory ({
Componente: DestaqueComponent,
Importações: [DestaqueComponentModule],
declarado: falso,}); O espectador fornece uma API conveniente para acessar as visualizações diferíveis ( @defer {} ).
Acesse o bloco de adiamento desejado usando o método spectator.deferBlock(optionalIndex) . O parâmetro optionalIndex é opcional e permite especificar o índice do bloco de adiamento que deseja acessar.
Acessando o primeiro bloco de adiamento : basta ligar para spectator.deferBlock() .
Acessando blocos de adiamento subsequente : use o índice correspondente como argumento. Por exemplo, spectator.deferBlock(1) acessa o segundo bloco (indexação baseada em zero).
O spectator.deferBlock(optionalIndex) retorna quatro métodos para renderizar diferentes estados do bloco de adiamento especificado:
renderComplete() - renderiza o estado completo do bloco de adiamento.
renderPlaceholder() - renderiza o estado de espaço reservado do bloqueio de adiamento.
renderLoading() - renderiza o estado de carregamento do bloco de adiamento.
renderError() - renderiza o estado de erro do bloco de adiamento.
Exemplo:
@Component ({Selector: 'App-cmp', modelo: `@Defer (na viewport) {<div> Estado completo do primeiro bloco de adiamento </div> <!-Parent Complete State->} @PlaceHolder { <div> espaço reservado </div>} `,}) classe DumMyComponent {} const CreateComponent = CreateComponentFactory ({componente: DummyComponent, adiFlockBeHavior: DeferblockBehavior.Manual,}); > {// Organize o espectador const = createComponent (); Para acessar os estados em blocos de adiamento aninhado, chame o encadeamento do método deferBlock do método de estado de bloco retornado.
Exemplo: Acessando o estado completo aninhado:
// assumindo `spectator.deferblock (0) .renderComplete ()` renderiza o estado completo do pai adiar blockconst parentCompleTestate = aguardar espectador.deferblock (). Rendercomplete (); // Acesso o estado completo do pai do painel BlockConstest NestedComComComCom = aguarda parentCompleTestate.renderComplete (). adjustBlock ();
Exemplo completo :
@Component ({Selector: 'App-cmp', modelo: `@Defer (na viewport) {<div> Estado completo do primeiro bloco de adiamento </div> <!-Parent State Complete-> @Defer {< Div> Estado completo do bloco de adiamento aninhado </div> <!-Estado completo aninhado->}} @placeholder {<div> espaço reservado </div>} `,}) classe DUMMYCONENT {} const CreateComponent = CreateComponentFactory ( {Componente: DummyComponent, adiSerblockBeHavior: adjustBlockBehavior.Manual,}); it ('deve renderizar o primeiro estado completo aninhado', assync () => {// organizar o Spectator = CreateComponent (); // ACT // Credores do pai Estado completo Const ParentCompleTestate = Aguarda Spectator.DeferBlock (). RenderComplete (); adiar bloco ');});Testar um componente com um componente host é uma técnica mais elegante e poderosa para testar seu componente. Basicamente, oferece a capacidade de escrever seus testes da mesma maneira que você escreve seu código. Vamos ver em ação:
importar {createHostFactory, SpectatorHost} de '@ngneat/spectator'; descrever ('zippyComponent', () => {
Deixe o espectador: SpectatorHost <zippyComponent>;
const createHost = createHostFactory (ZippyComponent);
it ('deve exibir o título da propriedade host', () => {spectator = createHost (`<zippy [title] =" title "> </zippy>`, {hostProps: {title: 'spectator é incrível'} }); espera (Spectator.Query ('. Zippy__title')). ToHaveText ('Spectator é incrível');
});
ele ('deve exibir a palavra "fechar" se abrir', () => {spectator = createHost (`<zippy title =" title zippy "> conteúdo zippy </zippy>`); spectator.click ('. Zippy__title' ); espera (Spectator.Query ('. Arrow')).
});}); O método host retorna uma instância do SpectatorHost que estende Spectator com a seguinte API adicional:
hostFixture - o jogo do host
hostComponent - a instância do componente do host
hostElement - o elemento nativo do host
hostDebugElement - O elemento de depuração do jogo do host
setHostInput - Altera o valor de um @Input() do componente host
queryHost - Leia mais sobre consulta em espectador
queryHostAll - Leia mais sobre consulta em espectador
Definir entradas diretamente em um componente usando setInput ou props não é possível ao testar com um componente host. As entradas devem ser definidas através do hostProps ou setHostInput e passadas para o seu componente no modelo.
Às vezes, é útil passar sua própria implementação de host. Podemos passar um componente host personalizado para o createHostFactory() que substituirá o padrão:
@Component ({Selector: 'Custom-host', modelo: ''}) classe CustomHostComponent {
title = 'personalizado hostComponent';} descrever ('com componente host personalizado', function () {
Deixe o espectador: SpectatorHost <ZippyComponent, CustomHostComponent>;
const createHost = createHostFactory ({componente: ZippyComponent, host: CustomHostComponent
});
ele ('deve exibir o título do componente host', () => {spectator = createHost (`<zippy [title] =" title "> </zippy>`); espera (spectator.query ('. zippy__title'))) .ToHaveText ('Host componente personalizado');
});}); Para componentes que usam o roteamento, existe uma fábrica especial disponível que estende a padrão e fornece um Stubbed ActivatedRoute para que você possa configurar opções de roteamento adicionais.
descrever ('ProductDetailSComponent', () => {
Deixe o espectador: SPECTATORROUTING <ProductDetailSComponent>;
const CreateComponent = CreaterOutingFactory ({componente: ProductDetailSComponent, params: {productId: '3'}, dados: {title: 'algum título'}
});
antes e cada vez (() => espectador = createComponent ());
it ('deve exibir o título dos dados da rota', () => {expect (spectator.query ('. title')). ToHaveText ('algum título');
});
ele ('deve reagir às mudanças de rotear', () => {spectator.setRouteParam ('productId', '5'); // seu teste aqui ...
});}); A API SpectatorRouting inclui métodos convenientes para atualizar a rota atual:
Interface SpectatorRouting <c> estende o espectador <C> {
/*** Simula uma navegação de rota atualizando os parâmetros, queryparams e fluxos observáveis de dados. */
Triggernavigation (Opções?: RouteOptions): void;
/*** Atualiza os parâmetros da rota e aciona uma navegação de rota. */
setRouteParam (nome: string, valor: string): void;
/*** Atualiza os parâmetros de consulta de rota e aciona uma navegação de rota. */
setRoutEqueryParam (Nome: String, Valor: String): void;
/*** Atualiza os dados da rota e aciona uma navegação de rota. */
setroutedata (nome: string, valor: qualquer): void;
/*** Atualiza o fragmento de rota e aciona uma navegação de rota. */
setrouttefragment (fragmento: string | null): void;
/*** Atualiza o URL da rota e aciona uma navegação de rota. */
setroutteurl (url: urlsegment []): void;}RouterTestingModule Se você definir a opção stubsEnabled como false , poderá passar uma configuração de roteamento real e configurar um teste de integração usando o RouterTestingModule do Angular.
Observe que isso requer promessas de resolver. Uma maneira de lidar com isso é fazer o seu teste async:
descreva ('teste de integração de roteamento', () => {
const CreateComponent = CreateroutingFactory ({Componente: MyComponent, Declarações: [OtherComponent], Stubsenabled: false, Rotas: [{Path: '', Component: MyComponent}, {Path: 'Foo', Component: OtherComponent}]
});
ele ('deve navegar usando o link do roteador', async () => {const espectador = createComponent (); // Aguarde as promessas de resolver ... aguarde espectador.fixture.whenStable (); // teste a rota atual por rota por afirmando o localExpect (Spectator.inject (Location) .Path ()). ToBe ('/'); // Clique em um linkspectator de roteador.Click ('. Link-1'); // Não se esqueça de esperar pelo promete resolver ... aguarde espectador.fixture.whenstable (); // Teste a nova rota afirmando o localExpect (Spectator.Inject (Location) .Path ()). Tobe ('/foo');
});}); A função createRoutesFactory pode levar as seguintes opções, além das opções de espectador padrão:
params : Params iniciais a serem usados no Stub ActivatedRoute
queryParams : Params de consulta inicial a serem usados no Stub ActivatedRoute
data : Dados iniciais a serem usados no Stub ActivatedRoute
fragment : Fragmento inicial para usar no Stub ActivatedRoute
url : segmentos iniciais de URL para usar no stub ActivatedRoute
root : o valor da root para o stub ActivatedRoute
parent : o valor para parent para o stub ActivatedRoute
children : o valor para children para o stub ActivatedRoute
firstChild : o valor para firstChild para o Stub ActivatedRoute
stubsEnabled (Padrão: true ): Ativa o stub ActivatedRoute , se definido como false , ele usa RouterTestingModule
routes : Se stubsEnabled estiver definido como false, você pode passar uma configuração Routes para RouterTestingModule
Existe uma fábrica de testes especiais para as diretivas de teste. Digamos que temos a seguinte diretiva:
@Directive ({Selector: '[Destaque]'}) Classe de exportação DestaqueDDirection {
@HostBinding ('style.background-color') BackgroundColor: String;
@HostListener ('MouseOver')
ONHOVER () {this.backgroundColor = '#000000';
}
@Hostlistener ('mouseout')
onLeave () {this.backgroundColor = '#ffffff';
}}Vamos ver como podemos testar as diretivas facilmente com o Spectator:
descrever ('DestactDirective', () => {
Deixe o espectador: Spectatordirective <ConsectDirection>;
const CreatedIRECTION = CreateRectionFactory (DestactDirective);
antes e cada vez (() => {Spectator = CreatedIREction (`<div Destaque> Teste de destaque Diretiva </div>`);
});
ele ('deve alterar a cor do fundo', () => {spectator.dispatchmouseevent (spectator.Element, 'mouseOver'); espera (spectator.Element) .toHavestyle ({BackgroundColor: 'rgba (0,0,0, 0.1 ) '}); Spectator.DispatchMouSeEvent (Spectator.Element,' MouseOut '); espera (Spectator.Element) .toHavestyle ({BackgroundColor:' #FFF '});
});
it ('deve obter a instância', () => {const instância = spectator.directive; espera (instância) .TobEDefined ();
});}); Definir entradas diretamente em uma diretiva usando setInput ou props não é possível. As entradas devem ser definidas através do hostProps ou setHostInput e passadas para sua diretiva no modelo.
O exemplo a seguir mostra como testar um serviço com o Spectator:
importar {CreateServiceFactory, Spectatorservice} de '@ngneat/spectator'; importar {automervice} de 'auth.service.ts'; descrever ('automervice', () => {
Deixe o espectador: Spectatorservice <uthService>;
const CreateService = CreateServiceFactory (AuthService);
antes e cada vez (() => espectador = createService ());
it ('não deve ser registrado em', () => {espera (spectator.service.isLoggedin ()). Tobefalsy ();
});}); A função createService() retorna SpectatorService com as seguintes propriedades:
service - Obtenha uma instância do serviço
inject() - um proxy para o angular TestBed.inject()
Também é possível passar um objeto com opções. Por exemplo, ao testar um serviço, você geralmente deseja zombar de suas dependências, pois nos concentramos no teste que está sendo testado.
Por exemplo:
@Injectable () Export Class AuthService {
Construtor (private DateService: DateService) {}
isLoggedin () {if (this.dateService.isexired ('timestamp')) {return false;} return true;
}} Nesse caso, podemos zombar da dependência DateService .
importar {CreateServiceFactory, Spectatorservice} de '@ngneat/spectator'; importar {automervice} de 'auth.service.ts'; descrever ('automervice', () => {
Deixe o espectador: Spectatorservice <uthService>;
const CreateService = CreateServiceFactory ({Service: AuthService, Provedores: [], EntryComponents: [], Mocks: [DateService]
});
antes e cada vez (() => espectador = createService ());
ele ('deve ser registrado', () => {const DateService = spectator.inject (dateService); dateService.isexpired.and.returnValue (false); espera (spectator.service.isLoggedin ()). Tobetuthy ();
});});O exemplo a seguir mostra como testar um tubo com espectador:
importar {SpectatorPipe, CreatePipeFactory} de '@ngneat/spectator'; importar {statsService} de './stats.service' ;mport {sumpipe} de' ./sum.pipe' ;describeaterriginar. {
Deixe o espectador: SpectatorPipe <SumpiPipe>;
const CreatePipe = CreatePipeFactory (Sumpipe);
ele ('deve resumir a lista fornecida de números (modelo)', () => {spectator = createPipe (`{{[1, 2, 3] | sum}}`); espera (spectator.Element) .toHaveTeText ('6');
});
it ('deve resumir a lista fornecida de números (prop)', () => {spectator = createPipe (`{{prop | sum}}`, {hostProps: {prop: [1, 2, 3]}} ); espera (espectador.Element) .toHaveText ('6');
});
it ('Deve delegar a soma do serviço', () => {const sum = () => 42; const Provider = {fornece: estatSservice, useValue: {sum}}; spectator = createpipe (`{{prop | sum}} `, {hostProps: {prop: [2, 40]}, provedores: [provedor]}); espera (espectador.Element) .toHaveText ('42 ');
});}); A função createPipe() retorna SpectatorPipe com as seguintes propriedades:
hostComponent - instância do componente host
debugElement - o elemento de depuração do acessório em torno do componente host
element - o elemento nativo do componente host
detectChanges() - um proxy para o angular TestBed.fixture.detectChanges()
inject() - um proxy para o angular TestBed.inject()
Definir entradas diretamente em um tubo usando setInput ou props não é possível. As entradas devem ser definidas através do hostProps ou setHostInput e passadas para o seu tubo no modelo.
O exemplo a seguir ilustra como testar um tubo usando um componente host personalizado:
importar {componente, input} de '@angular/core'; importar {SpectatorPipe, CreatePipeFactory} de '@ngneat/spectator'; importar {médiapipe} de './average.pipe' ;mport {statsService} de' ./stats .Service ';@componente ({
Modelo: `<div> {{prop | AVG}} </div> `}) classe CustomHostComponent {
@Input () public Prop: número [] = [1, 2, 3];} descrever ('médiapipe', () => {
Deixe o espectador: SpectatorPipe <AuxEpipe>;
const CreatePipe = CreatePipeFactory ({Pipe: Médiapipe, Host: CustomHostComponent
});
ele ('deve calcular a média de uma determinada lista de números', () => {spectator = createPipe (); espera (spectator.Element) .toHaveText ('2');
});
ele ('deve resultar em 0 quando a lista de números estiver vazia', () => {spectator = createPipe ({hostProps: {prop: []}}); espera (spectator.Element) .toHaveText ('0');
});
it ('Deve delegar o cálculo ao serviço', () => {const avg = () => 42; const Provider = {fornece: estatsSservice, useValue: {avg}}; espectador = createpipe ({provedores: [provedor ]}); espera (Spectator.Element) .toHaveText ('42 ');
});});Para cada fábrica de espectadores, podemos facilmente zombar de qualquer provedor.
Todo serviço que passamos para a propriedade mocks será ridicularizado usando a função mockProvider() . A função mockProvider() converte cada método em um espião de jasmim. (ou seja, jasmine.createSpy() ).
Aqui estão alguns dos métodos que expõe:
dateService.isexpired.and.callthrough (); dateService.isexired.and.callfake (() => falso); dateService.isexired.and.throwerRor ('erro'); dateService.isexpired.andcallfake () => falso); ; No entanto, se você usar o JEST como estrutura de teste e desejar utilizar seu mecanismo de zombaria, importe o mockProvider() do @ngneat/spectator/jest . Isso usará automaticamente a função jest.fn() para criar uma simulação compatível com jest.
mockProvider() não inclui propriedades. Caso você precise ter propriedades em sua simulação, você pode usar o segundo argumento:
const CreateService = CreateServiceFactory ({
Serviço: AuthService,
Provedores: [MockProvider (outros serviços, {nome: 'martin', emissor: novo sujeito (), rockedmethod: () => 'rocked'})
],});Se um componente depende de um serviço ridículo no método do ciclo de vida do Oninit, a detecção de alterações precisará ser desativada até que os serviços sejam injetados.
Para configurar isso, altere o método createComponent para que a opção detectChanges seja definida como falsa e, em seguida, chame manualmente detectChanges no espectador após a configuração dos serviços injetados.
const CreateComponent = CreateComponentFactory ({
Componente: WeatherDashboardComponent}); It ('deve chamar a API do clima no init', () => {
const espectador = createComponent ({detectChanges: false
});
const weatherService = spectator.inject (WeatherDataapi);
WeatherService.getweatherdata.andreturn (de (Mockweatherdata));
espectador.DetectChanges ();
espera (WEATHERSERVICE.GETWEATHTATA) .TO -HAVERBEENCALLED ();});Se um componente depende de um serviço ridículo em seu construtor, você precisa criar e configurar a simulação e fornecer a simulação ao criar o componente.
const CreateComponent = CreateComponentFactory ({
Componente: WeatherDashboardComponent}); It ('deve chamar a API meteorológica no construtor', () => {
const weatherService = CreateSpyObject (WeatherDataapi);
WeatherService.getweatherdata.andreturn (de (Mockweatherdata));
espectador = createComponent ({provedores: [{fornece: WeatherDataapi, UseValue: WeatherService}]
});
espera (WEATHERSERVICE.GETWEATHTATA) .TO -HAVERBEENCALLED ();});Por padrão, o Spectator usa Jasmine para criar espiões. Se você estiver usando o JEST como estrutura de teste, poderá deixar o Spectator criar espiões compatíveis com jest.
Basta importar uma das seguintes funções de @ngneat/spectator/jest (em vez de @ngneat/espectador) e usará o piadas em vez de jasmim. createComponentFactory() , createHostFactory() , createServiceFactory() , createHttpFactory() , mockProvider() .
importar {CreateServiceFactory, SpectatorService} de '@ngneat/spectator/jest'; importar {authservice} de './auth.service' ;mport {DateService} de' ./date.service'; => {
Deixe o espectador: Spectatorservice <uthService>;
const CreateService = CreateServiceFactory ({Service: AuthService, Mocks: [DateService]
});
antes e cada vez (() => espectador = createService ());
ele ('não deve ser conectado', () => {const DateService = spectator.inject <dateService> (dateService); dateService.isexpired.mockreturnValue (true); espera (spectator.service.isLoggedin ()). );
});
ele ('deve ser conectado', () => {const DateService = spectator.inject <dateService> (dateService); dateService.isexpired.mockreturnValue (false); espera (espectador.Service.islogledin ()). ToBetruthy () ;
});}); Ao usar o esquema do componente, você pode especificar o sinalizador --jest para que as importações de idiotas sejam usadas. Para despertar o padrão, atualize angular.json :
"Esquema": {"@ngneat/espectador: espectador-componente": {"jest": true
}
}O espectador facilita muito o teste de serviços de dados, que usam o módulo HTTP angular, muito mais fácil. Por exemplo, digamos que você tem serviço com três métodos, um executa um Get, um post e um executa solicitações simultâneas:
Classe de exportação Todosdataservice {
Construtor (privado httpClient: httpclient) {}
getTodos () {return this.httpclient.get ('api/Todos');
}
PostTODO (id: número) {return this.httpclient.post ('api/Todos', {id});
}
collectTodos () {return mescle (this.httpclient.get ('/api1/Todos'), this.httpclient.get ('/api2/Todos'));
}}O teste para o serviço acima deve parecer:
importar {createhttpFactory, httpmethod} de '@ngneat/spectator'; importar {TodosDataservice} de './todos-data.service' ;Describe('httpclient testing', () => {
Deixe o espectador: espectatorhttp <dosdataService>;
const createhttp = createhttpFactory (TODOSDATASERVICE);
antes e cada vez (() => espectador = createhttp ());
it ('pode testar httpclient.get', () => {spectator.service.gettodos (). subscribe (); spectator.expectone ('api/Todos', httpmethod.get);
});
ele ('pode testar httpclient.post', () => {spectator.service.posttodo (1) .subScribe (); const req = spectator.expectone ('api/Todos', httpmethod.post); request.body ['id']). ToEqual (1);
});
It ('pode testar as solicitações atuais http', () => {spectator.service.gettodos (). subscribe (); const reqs = spectator.expectCurrent ([{url: '/api1/Todos', método: httpmethod.get }, {Url: '/api2/Todos', método: httpmethod.get}]); spectator.flushall (reqs, [{}, {}, {}]);
});}); Precisamos criar uma fábrica HTTP usando a função createHttpFactory() , passando o serviço que você deseja testar. O createHttpFactory() retorna uma função que pode ser chamada para obter uma instância de espectadorhttp com as seguintes propriedades:
controller - Um proxy para HttpTestingController angular
httpClient - um proxy para HttpClient angular
service - a instância de serviço
inject() - um proxy para o angular TestBed.inject()
expectOne() - Espere que tenha sido feita uma única solicitação que corresponda ao URL fornecido e ao seu método e retorne seu pedido de simulação
É possível definir injeções que estarão disponíveis para cada teste, sem a necessidade de desconquá-las em cada teste:
// test.tsimport {defineglobalsInjections} de '@ngneat/spectator'; importar {translocomodule} de '@ngneat/transloco'; defineglobalsinjeções ({{
importações: [translocomodule],}); Esteja ciente de que o defineGlobalsInjections() deve ser chamado antes que os módulos sejam carregados. No test.ts angular padrão.
context.Keys (). Mapa (contexto);
Por padrão, os provedores de componentes originais (por exemplo, os providers no @Component ) não são tocados.
No entanto, na maioria dos casos, você deseja acessar os provedores do componente em seu teste ou substituí -los por maquetes.
Por exemplo:
@Componente({
modelo: '...',
Provedores: [fooservice]}) classe foocomponent {
Construtor (Private Fooservice: Fooservice} {}
// ...} Use os componentProviders para substituir o provedor FooService :
const CreateComponent = CreateComponentFactory ({
componente: foocomponent,
ComponentProviders: [{fornece: fooservice, useValue: SomethingElse}
]}) Ou zombar do serviço usando componentMocks :
const CreateComponent = CreateComponentFactory ({
componente: foocomponent,
componentMocks: [fooservice]}); Para acessar o provedor, obtenha -o do injetor do componente usando o parâmetro fromComponentInjector :
espectador.Inject (Fooservice, verdadeiro)
Da mesma maneira, você também pode substituir os provedores de visualização de componentes usando os componentViewProviders e componentViewProvidersMocks .
As mesmas regras também se aplicam às diretivas usando os parâmetros directiveProviders e directiveMocks .
Espere ('. Zippy__content'). Not.toexist (); espera ('. Zippy__content'). Zippy ')). )). ). Se a ordem for irrelevante, desative o modo rigoroso manualmente. zippy__content '). não. ) .Not.ToHaveclass (['Class-B', 'Class-A']); Espere ('. Zippy__content'). ToHaveclass ('Class-A, Class-B', {Strict: false}); espera ('. Zippy__content'). zippy__content '). , {Strict: false}); // Observe que o ToHaveText procura apenas a existência de uma string, não se a string for exatamente a mesma. Se você deseja verificar se a string é completamente a mesma, use ToHaveExactText.// Observe que, se você deseja verificar se a string é completamente a mesma, mas aparada primeiro, use TOHaveExActTriMedText.// Observe que, se você passar vários valores, O espectador verifica o texto de cada elemento da matriz em relação ao índice do elemento encontrado. ']); espera ('. Zippy__content ') ') .tocontaintext ([' content a ',' content b ']); espera ('. Content b '])); espera ('. Zippy__content '). . ('.zippy__content'). ) .toHavestyle ({BackgroundColor: 'rgba (0, 0, 0, 0.1)'}); espera ('. Zippy__content'). .CheckBox '). Tobechecked (); espera ('. .tobehidden (); espera ('elemento'). Tobeselected (); // Observe que, devido a restrições no JEST (não aplicando a lógica real do layout no DOM virtual), certos correspondentes podem resultar em falsos positivos. Por exemplo, largura e altura definidas como 0Expect ('elemento'). Tobevisible (); espera ('entrada'). TOBEFOCUSED (); espera ('div'). Tobematchedby ('. componente.Object) .TobePartial ({Aproperty: 'Avalue'}); espera ('div'). 'texto'});Gere componente, serviço e diretiva com modelos de espectadores espectadores com CLI angular: (ao usá -lo como padrão)
Componente
Especificação padrão: ng g cs dashrized-name
Especificação com um host: ng g cs dashrized-name --withHost=true
Especificação com um host personalizado: ng g cs dashrized-name --withCustomHost=true
Serviço:
Especificação padrão: ng g ss dashrized-name
Especial para testar o serviço de dados http: ng g ss dashrized-name --isDataService=true
Diretiva:
ng g ds dashrized-name
Para usar spectator como a coleção padrão em seu projeto Angular CLI, adicione -o ao seu angular.json :
ng config cli.defaultCollection @ngneat/espectador
Os esquemas spectator estendem a coleção padrão @schematics/angular . Se você deseja definir padrões para esquemas, como gerar componentes com o arquivo SCSS, deve alterar o nome do pacote esquemático de @schematics/angular para @ngneat/spectator em angular.json :
"Esquema": {"@ngneat/espectador: espectador-componente": {"style": "scss"
}
}Os exemplos no Karma do Angular Docs Testing Developer Guide foram reproduzidos em espectador e brincadeira. (Por conveniência, esta é a versão local dos exemplos de karma.)
A versão Spectator & Jest pode ser acessada aqui.
Netanel basal | Dirk Luijk | Ben Elliott |
Obrigado a essas pessoas maravilhosas (key emoji): ~~~~
I. Sinai ? ? | Valentin Buryakov ? | Ben Grynhaus ? | Martin Nuc | Lars Gyrup Brink Nielsen ? | Andrew Grekov ? | Jeroen Zwartepoorte |
Oliver Schlegel | Rex ye ? | Tchmura | Yoeri nijs | Anders Skarby | Gregor Woiwode | Alexander Sheremetev ? |
Microfone | Mehmet Erim | Brett Eckert | Ismail Faizi | Maxime | Jonathan Bonnefoy | Ferry de Colum |
Chris Cooper | Marc Scheib | DGSmith2 | Dedwardstech ? | Tamasfoldi ? | Paolo Caleffi | Toni Villena |
Iay oded | Guillaume de Jabrun | Anand Tiwary | Ales Doganoc | Zoltan | Vitalii Baziuk | ClementLemarc-Certua |
Yuriy Grunin | Andrey Chalkin | Steven Harris | Richard Sahrakorpi | Dominik Kremer | Mehmet Ozan Turhan | Vlad Lashko |
William Tjondrosuharto | Chaz Gatian | Pavel Korobov | Enno Lohmann | Pawel Boguslawski | Tobias Wittwer | Mateo Tibaquirá |
Este projeto segue a especificação de todos os contribuintes. Contribuições de qualquer tipo de boas -vindas!