React Native e Detox

Criando testes E2E para simular seu usário!

Eduardo Rabelo
6 min readJan 15, 2018
Detox, criando testes E2E de modo simples e eficaz!

React Native tem um ecossistema incrível, existem vários recursos para acessar e todo dia, um artigo novo pipoca no Medium.

No meio de tudo isso, algumas bibliotecas se destacam. Hoje vamos ver uma biblioteca criada pela galera do Wix.com, focada especialmente para testes funcionais/E2E, simulando o uso do seu app como um usuário final, é a famosa Detox.

Detox é uma biblioteca que cria um ambiente de teste seguro e isolado (gray box tests), possibilitando que você crie testes funcionais de ponta a ponta (functional tests / e2e tests).

Como os testes são escritos

Se você já escreveu testes em Mocha ou Jest, eles são bem parecidos:

describe('Tela Inicial', () => {

it('deve mostrar o texto correto', async () => {
await device.reloadReactNative();
await expect(element(by.text('Olá, seja bem-vindo!'))).toBeVisible();
});

});

Incrível não? O mais interessante, é que por trás desse teste, temos algumas etapas como:

Facilitando e muito a nossa vida ao escrever testes não-unitários.

Instalando as depêndencias

Nesse exemplo, iremos rodar um projeto no simulador do iOS. Iremos falar sobre Android em um artigo futuro. Para usar Detox com iOS, os requesitos básicos são:

1. Sistema Operacional:

  • Mac com macOS, no mínimo, El Captain 10.11
  • Xcode 8.3 com a linha de comando do Xcode instalada (para verificar se você já tem ela instalada, você pode rodar gcc -v, um alerta do sistema irá aparecer caso você não tenha).

2. Instale a versão mais recente do Homebrew

Acessando https://brew.sh/

3. Tenha o Node.js 7.6.0+ instalado

Você pode utilizar Homebrew para isso:

brew update && brew install node

Verifique se está corretamente instalado rodando node -v (lembre-se, acima de 7.6.0+).

4. Instale appleSimUtils

Uma coleção de ferramentas para os simuladores da Apple. Detox usa essa interface para se comunicar com eles:

brew tap wix/brew
brew install --HEAD applesimutils

Verifique se está corretamente instalado rodando applesimutils no seu terminal.

Iniciando um projeto

Assim como no artigo anterior. Não irei falar sobre os requesitos básicos para instalar React Native na sua máquina. Para isso, eu recomendo você ir na documentação oficial e seguir o Getting Started.

Vamos iniciar um novo projeto:

react-native init ExemploDetox

Vamos instalar o pacote do Detox e, por padrão, a equipe do Detox recomenda o uso do Mocha para rodar os testes:

cd ExemploDetox
npm install --save-dev detox mocha

Vamos adicionar a configuração básica ao nosso package.json:

...
"detox": {
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/NOME_DO_SEU_APP.app",
"build": "xcodebuild -project ios/NOME_DO_SEU_APP.xcodeproj -scheme NOME_DO_SEU_APP -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"name": "iPhone 7"
}
}
}
...

Perceba a chave NOME_DO_SEU_APP acima, e substitua ela pelo nome do seu projeto, no nosso exemplo, iremos alterar para: ExemploDetox.app, ExemploDetox.xcodeproj e ExemploDetox respectivamente.

Outro ponto importante, é saber se o simulador selecionado (nesse caso iPhone 7), está instalado na sua máquina. Para verificar quais simuladores você tem, você pode rodar xcrun simctl list no seu terminal para listar os simuladores disponíveis.

Agora, iremos pedir para o Detox criar seus arquivos iniciais com:

./node_modules/.bin/detox init

Aqui você tem outras opções como:

  • Pode instalar o detox-cli globalmente com npm install -g detox-cli e rodar detox init
  • Utilizar yarn para rodar os binários instalados no seu node_modules com yarn run detox init
  • Adicionar um npm script para fazer o link do binário com sua linha de comando e utilizar npm run detox (para passar parâmetros, utilize -- )
...
"scripts": {
...
"detox": "detox"
}
...

Para facilitar nossa uso, vamos adicionar alguns scripts:

...
"scripts": {
...
"detox:build": "detox build",
"detox:test": "detox test"
}
...

Estrutura inicial

Antes de rodar nosso comando, vamos tirar alguns minutos para conhecer a estrutura inicial gerada pelo Detox. Já instalamos as depêndencias de sistema, de projeto e iniciamos uma estrutura básica. Teremos uma pasta e2e localizada na raíz do nosso projeto:

.babelrc
.buckconfig
.flowconfig
.gitattributes
.gitignore
.watchmanconfig
App.js
__tests__
android
app.json
e2e // <= pasta criada pelo "detox init"
index.js
ios
node_modules
package-lock.json
package.json
yarn.lock

Os arquivos iniciais são:

firstTest.spec.js
init.js
mocha.opts

Vamos abrir o firstTest.spec.js:

describe('Example', () => {
beforeEach(async () => {
await device.reloadReactNative();
});

it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});

it('should show hello screen after tap', async () => {
await element(by.id('hello_button')).tap();
await expect(element(by.text('Hello!!!'))).toBeVisible();
});

it('should show world screen after tap', async () => {
await element(by.id('world_button')).tap();
await expect(element(by.text('World!!!'))).toBeVisible();
});
})

Para entender melhor o que está acontecendo, vamos analisar essa suíte de testes.

Analisando exemplo inicial

  • Temos um gancho beforeEach que diz ao Detox para recarregar o seu aplicativo React Native antes de cada teste na sua suíte.
beforeEach(async () => {
await device.reloadReactNative();
});
  • Nosso primeiro teste que contém uma assertação que busca por um elemento pelo seu id, element(by.id(...)) e espera que ele esteja visível, expect(….).toBeVisible():
it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});

Para fazer esse teste passar, precisamos adicionar um testID com valor “welcome” na primeira tela do nosso projeto. Vamos abrir App.js e adicionar uma propriedade testID="welcome" :

<View testID='welcome' style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit index.ios.js</Text>
<Text style={styles.instructions}>Press Cmd+R to reload,{'\n'}Cmd+D or shake for dev menu</Text>
</View>
  • Nosso segundo teste é:
it("should show hello screen after tap", async () => {
await element(by.id("hello_button")).tap();
await expect(element(by.text("Hello!!!"))).toBeVisible();
});
  • Primeiro busca um elemento com testID de valor “hello_button”, executa um método .tap()
  • Em seguida, ele busca um elemento que contenha um texto na tela atual pelo valor “Hello!!!”, element(by.text(...)).
  • Esse elemento deve estar visível, executando .toBeVisible();

O terceiro teste é semelhante ao descrito acima. Nós não iremos adicionar o estado/botão necessário para fazer esses testes passarem. Fica o exercício para o leitor ;)

Detox oferece uma gama de métodos para ações em um elemento, encontrar um elemento na tela atual e assertações.

Você pode conferir a documentação oficial, que é bem completa!

Executando nossos testes

Se você alterou os nomes corretamente como mostrado anteriorment (NOME_DO_SEU_APP), vamos executar:

npm run detox:build && \ [A]
npm run detox:test [B]
  • [A] o comando executado, detox build , irá executar o Metro Bundler (caso não haja nenhum atualmente) e disponibilizar seu projeto
  • [B] irá executar o seu aplicativo isoladamente, com logs no seu terminal

As seguintes etapas devem ocorrer:

  • o comando debox build deve executar o Metro Bundler e compilar seu projeto
Iniciando os comandos
  • o comando debox test deve consumir seu projeto e começar a execução dos testes
Detox rodando o Metro Bundler e iniciando os testes
  • como são testes de exemplo, eles devem falhar
Os testes de exemplo devem falhar

Fazendo nossos testes passarem

Se você chegou até aqui, viu que adicionamos um testID com valor “welcome” na nossa tela principal. Com isso, vamos deletar os outros dois testes exemplo e atualizar firstTest.spec.js para:

describe("Example", () => {
beforeEach(async () => {
await device.reloadReactNative();
});
it("should have welcome screen", async () => {
await expect(element(by.id("welcome"))).toBeVisible();
});
});

E ao rodar npm run detox:test novamente, teremos o seguinte resultado:

Testes estão verdes! :P

Yaaahoo! Concluimos a integração inicial de Detox a seu projeto React Native! 🎉🎉🎉

Finalizando

Como mencionei acima, a documentação do Detox é incrível e com exemplos de fluxo de trabalho para seu dia-a-dia.

E como disse Guillermo Rauch:

Detox é a ferramenta que faltava para completar seu ciclo de testes.

Vale a pena seguir o Tal Kol e Rotem Mizrachi-Meidan para ficar por dentro das últimas novidades da equipe de engenharia do Wix.

Quer contribuir? Acesse o repositório oficial ou dê um ping no Twitter Wix Engineering.

--

--