O que eu gosto no ecossistema React é que a IDEA está por trás de muitas decisões. Vários autores escrevem vários artigos em apoio à ordem existente e explicam por que tudo está “certo”, para que todos entendam que a festa está no caminho certo.
Depois de algum tempo, a IDEA muda um pouco e tudo começa do começo.
E o começo desta história é a separação de componentes em contêineres e não contêineres (popularmente chamados Dumb Components, desculpe pelo meu francês).

O problema
O problema é muito simples - testes de unidade. Recentemente, houve algum movimento em direção aos testes de integração - bem, você sabe "Escrever testes. Não muitos. Principalmente integração". . Esta não é uma má ideia, e se o tempo for curto (e os testes não forem particularmente necessários) - é isso que você precisa fazer. Vamos chamar de testes de fumaça - para verificar se nada parece explodir.
Se houver muito tempo e forem necessários testes, é melhor não seguir esse caminho, porque escrever bons testes de integração é muito, muito longo. Só porque eles crescerão e crescerão, e para testar o terceiro botão à direita, primeiro você precisará clicar em 3 botões no menu e não se esqueça de fazer login. Em geral - aqui está uma explosão combinatória em uma bandeja de prata.
A solução aqui é uma e simples (por definição) - testes de unidade. A capacidade de iniciar testes com algum estado pronto de alguma parte do aplicativo. Mais precisamente, reduzir (restringir) a área de teste do Aplicativo ou do Big Block para algo pequeno - uma unidade, não importa o que seja. Não é necessário usar enzimas - você pode executar testes no navegador, se a alma perguntar. A coisa mais importante aqui é poder testar algo isoladamente . E sem muita dificuldade.
O isolamento é um dos pontos-chave no teste de unidade, e é por isso que os testes de unidade não gostam. Eles não gostam disso por várias razões:
- por exemplo, sua "unidade" é removida do aplicativo e não funciona em sua composição, mesmo quando seus próprios testes são verdes.
- ou, por exemplo, porque o isolamento é um cavalo tão esférico no vácuo que ninguém viu. Como alcançá-lo e como mensurá-lo?
Pessoalmente, não vejo problemas aqui. No primeiro parágrafo, é claro, você pode recomendar testes de integração, eles foram inventados para isso - para verificar como os componentes pré-testados são montados corretamente. Você confia nos pacotes npm que testam, é claro, apenas eles próprios e não eles próprios como parte do seu aplicativo. Como seus "componentes" diferem dos pacotes "não seus"?
Com o segundo parágrafo, tudo é um pouco mais complicado. E este artigo será exatamente sobre esse ponto (e tudo o que era antes - uma introdução) - sobre como tornar uma unidade " unitária " testável .
Dividir e conquistar
A idéia de separar os componentes do React em "Container" e "Presentation" não é nova, bem descrita e já conseguiu ficar um pouco desatualizada. Se tomarmos como base (o que 99% dos desenvolvedores fazem) um artigo de Dan Abramov , o Componente de Apresentação:
- Preocupam-se com a aparência das coisas
- Podem conter componentes de apresentação e de contêiner
**
internos e geralmente têm sua própria marcação e estilos DOM) - Slots de suporte (muitas vezes permitem a contenção por this.props.children)
- Independente do aplicativo (não possui dependências no restante do aplicativo, como ações ou lojas do Flux)
- Não dependa de dados (não especifique como os dados são carregados ou alterados)
- A interface é baseada em adereços (Receba dados e retornos de chamada exclusivamente via adereços)
- Frequentemente sem estado (raramente têm seu próprio estado (quando o fazem, é o estado da interface do usuário em vez de dados))
- Frequentemente SFC (são escritos como componentes funcionais, a menos que precisem de estado, ganchos de ciclo de vida ou otimizações de desempenho)
Bem, os contêineres são toda a lógica, todo o acesso aos dados e todo o aplicativo em princípio.
Em um mundo ideal, os contêineres são o tronco e os componentes da apresentação são as folhas.
Existem dois pontos principais na definição de Dan: "Independente de aplicativo" , que é quase uma definição acadêmica de "unidade" e * "Pode conter outros componentes de apresentação e recipientes **
" *, onde essas estrelas são especialmente interessantes.
(tradução gratuita) ** Nas versões anteriores do meu artigo, eu (Dan) disse que os componentes de apresentação deveriam conter apenas outros componentes de apresentação. Acho que não. O tipo de componente é os detalhes e pode mudar com o tempo. Em geral, não compartilhe e tudo ficará bem.
Vamos lembrar o que acontece depois disso:
- No livro de histórias, tudo cai, porque algum tipo de contêiner, no terceiro botão à esquerda, rasteja para o lado do qual não há nenhum. Saudações especiais para graphql, react-router e outros react-intl.
- A capacidade de usar mount nos testes é perdida, porque renderiza tudo de A a Z e, novamente, em algum lugar nas profundezas da árvore de renderização, alguém faz alguma coisa e os testes caem.
- A capacidade de controlar o estado do aplicativo é perdida, pois (figurativamente falando) a capacidade de molhar seletores / resolvedores (especialmente com proxyquire) é perdida e todo o portão precisa estar molhado. E isso é legal para testes de unidade.
Se você acha que os problemas são um pouco exagerados, tente trabalhar em equipe quando esses contêineres, que serão usados nos seus não contêineres, mudarem em outros departamentos e, como resultado, você e eles analisam os testes e não conseguem entender por que ontem tudo funcionou, e agora novamente.
Como resultado, você deve usar o superficial, que por design elimina todos os efeitos colaterais prejudiciais (e inesperados). Aqui está um exemplo simples do artigo "Por que eu sempre uso superficial"
Imagine que a dica de ferramenta renderize "?". Quando clicado, o próprio tipo será mostrado.
import Tooltip from 'react-cool-tooltip'; const MyComponent = () => { <Tooltip> hint: {veryImportantTextYouHaveToTest} </Tooltip> }
Como testá-lo? Monte + clique + verifique o que é visível. Este é um teste de integração, não uma unidade, e a questão é como clicar em um componente "externo" para você. Não há problema com o superficial, pois não há cérebro e o próprio "componente alienígena". Mas existem cérebros aqui, já que a dica de ferramenta é um contêiner, enquanto o MyComponent é praticamente uma apresentação.
jest.mock('react-cool-tooltip', {default: ({children}) => childlren});
Mas se você reagir, dica legal, não haverá problemas com o teste. O "componente" tornou-se muito mais estúpido, muito mais curto, muito mais finito .
Componente final
- um componente com um tamanho conhecido, que pode incluir outros componentes finais anteriormente conhecidos ou que não os contenham.
- não contém outros contêineres, pois eles contêm um estado não controlado e um tamanho "aumentado", ou seja, tornar o componente atual infinito .
- caso contrário, é um componente de apresentação regular. De fato, exatamente como descrito na primeira versão do artigo de Dan.
O componente final é apenas uma engrenagem retirada de um grande mecanismo.
A questão toda é como retirá-lo.
Solução 1 - DI
Meu favorito é injeção de dependência. Dan também o ama . Em geral, isso não é DI, mas "slots". Em poucas palavras - não é necessário usar recipientes dentro da apresentação - eles precisam ser injetados lá. E nos testes será possível injetar outra coisa.
É exatamente esse o caso quando "os contêineres são o tronco e os componentes da apresentação são folhas"
Solução 2 - Limites
O DI geralmente pode ser legal. Provavelmente agora% username% pensa em como pode ser aplicado na base de código atual e a solução não foi inventada ...
Nesses casos, o Borders salvará você.
const Boundary = ({children}) => ( process.env.NODE_ENV === 'test' ? null : children
Aqui, em vez de "slots", todos os "pontos de transição" simplesmente se transformam em Fronteira, que renderiza qualquer coisa durante os testes. Declarativamente , e exatamente o que você precisa para "tirar o equipamento".
Solução 3 - Camada
As bordas podem ser um pouco difíceis, e pode ser mais fácil torná-las um pouco mais inteligentes adicionando um pouco de conhecimento sobre a Camada.
const checkTier = tier => tier === currentTier; const withTier = tier => WrapperComponent => (props) => ( (process.env.NODE_ENV !== 'test' || checkTier(tier)) && <WrapperComponent{...props} /> ); const PageChrome = () => ( <section> <aside><ASideContainer /></aside> <Page /> </section> ); const ASideContainer = withTier('UI')(...) const Page = withTier('Page')(...) const PageChromeContainer = withTier('UI')(PageChrome);
Sob o nome Camada / Camada, pode haver coisas diferentes - recurso, duck, módulo ou apenas essa camada / camada. O ponto não é importante, o principal é que você pode puxar a marcha, talvez não uma, mas o número final, de alguma forma traçando uma linha entre o que você precisa e o que não precisa (para testes diferentes, essa é uma borda diferente).
E nada impede de marcar esses limites de alguma maneira diferente.
Solução 4 - Preocupações separadas
Se a solução (por definição) reside na separação de entidades - o que acontecerá se as tomarmos e separarmos?
Os "contêineres", dos quais tanto não gostamos, são geralmente chamados de contêineres . E se não - nada impede agora de começar a nomear componentes de alguma maneira mais sonora. Ou eles têm um determinado padrão em seu nome - Connect (WrappedComonent) ou GraphQL / Query.
E se, em tempo de execução, traçar uma linha entre entidades com base em um nome?
const PageChrome = () => ( <section> <aside><ASideContainer /></aside> <Page /> </section> );
Além de uma linha nos testes, o reag-remock removerá todos os recipientes que possam interferir nos testes.
Em princípio, essa abordagem pode ser usada para testar os próprios contêineres - você só precisa remover tudo, exceto o primeiro.
import {createElement, remock} from 'react-remock';
Mais uma vez - algumas linhas e equipamentos removidos.
Total
No ano passado, o teste de componentes do React se tornou mais complicado, especialmente para montagem - você precisa sobrescrever todos os 10 provedores, contextos e está ficando cada vez mais difícil testar o componente certo no estilo certo - há muitas cordas para puxar.
Alguém cospe e entra no mundo raso. Alguém acenou com as mãos em testes de unidade e transferiu tudo para Cypress (ande como ande!).
Outra pessoa cutuca um dedo com a reação, diz que esses são efeitos algébricos e você pode fazer o que quiser. Todos os exemplos acima são essencialmente o uso desses efeitos e zombes algébricos . Para mim e DI estes são moki.
PS: Este post foi escrito como uma resposta aos comentários no React / RFC sobre o fato de que a equipe do React quebrou tudo, e todos os polímeros de lá também
PPS: Este post é na verdade uma tradução muito gratuita de outro
PPPS: em geral, para isolamento real, observe o rewiremock