Alura > Cursos de Mobile > Cursos de iOS > Conteúdos de iOS > Primeiras aulas do curso iOS: melhorando o app com testes de unidade e TDD

iOS: melhorando o app com testes de unidade e TDD

Funcionalidade de busca - Apresentação

Olá, boas-vindas a mais um curso de iOS na Alura, ministrado pelo Ândriu Coelho.

Audiodescrição: Ândriu é uma pessoa branca de olhos e cabelos castanhos. Usa barba e bigode. Está com uma camiseta azul escuro com o logo da Alura. Ao fundo, temos uma estante do lado direito com objetos diversos de decoração, dentre eles vasos de plantas, livros, abajures e a letra A na fonte da logomarca da a Alura. Do lado esquerdo, há uma planta em um vaso e a parede está iluminada por luzes nas tonalidades azul e rosa.

O que vamos aprender

Neste curso, vamos trabalhar com testes de unidade. Vamos usar o projeto Chef Delivery, que simula um aplicativo de entrega de refeições.

O aplicativo tem, por exemplo, uma aba com uma tela de busca, uma lista simples, mas que conseguimos explorar os fundamentos de teste de unidade. A ideia é começar com uma tarefa para conseguir buscar os restaurantes. Podemos buscar, por exemplo, por nome, como "Casa do Sushi" ou qualquer outro restaurante. Vamos começar a estudar os conceitos de teste de unidade.

Depois disso, vamos aprofundar um pouco mais sobre testes, falando sobre o fluxo de trabalho do Test-Driven Development (Desenvolvimento Orientado por Testes), onde vamos começar pelo teste, seguido da classe de produção, onde faremos os ajustes para o teste passar, e por último vamos fazer uma refatoração no nosso código.

Vamos usar como exemplo a mesma busca, mas adicionando um pouco mais de funcionalidade. Além de conseguir buscar por nome, vamos conseguir buscar por produto. Se digitarmos, por exemplo, "pizza", conseguiremos ver quais são os restaurantes ou lanchonetes que fornecem esse tipo de alimento.

Ou se buscarmos por outra palavra, como "sushi", veremos quais são os estabelecimentos associados a esse termo que acabamos de digitar.

Também vamos falar um pouco sobre teste de fluxo de exceção, ou seja, métodos que disparam exceção e, por fim, falaremos da qualidade dos testes.

Pré-requisitos para o curso

É interessante que você já conheça a Linguagem Swift, que você saiba construir layouts com Swift UI e compreenda como fazer requisições HTTP no aplicativo.

Esperamos você no próximo vídeo!

Funcionalidade de busca - Conhecendo o projeto e a nova tela

Para iniciar, a ideia é apresentar o projeto que vamos utilizar durante nosso curso de testes de unidade na Alura. Se você já fez o curso de SwiftUI (Interface do Usuário Swift) na nossa formação de layouts para iOS, já deve conhecer esse projeto, o Chef Delivery. A ideia desse projeto é simular um aplicativo de entrega de refeições.

Visão geral do aplicativo

Ele tem a tela de onboarding, que mostra algumas informações do app. Se fizermos um scroll para a lateral direita da tela, entramos na tela inicial, que é a tela principal do nosso app, a home.

Mockup da página inicial do aplicativo Chef Delivery. Na parte superior da tela, barra de navegação com endereço 'R. Vergueiro, 3185' e ícone de sino de notificações. Abaixo, uma grade com ícones de categorias de produtos e seus nomes: Restaurantes, Farmácia, Descontos, Gourmet, Mercado, Pet, Bebidas. Abaixo, um banner de propaganda de Pokes com 40% de desconto. Depois, uma lista de título 'Lojas' contendo os restaurantes 'Monstro burger', 'Food court' e 'Carbron', nessa ordem. Por fim, uma barra inferior com um ícone de casa rotulado 'Início' e um ícone de lupa rotulado 'Busca'.

Até aqui nenhuma novidade, mas a ideia desse curso é abordarmos algumas regras que vamos implementar em uma funcionalidade nova que vamos desenvolver: a funcionalidade de busca.

Na barra inferior, quando clicamos no ícone da lupa (opção de busca), abrimos a tela onde listamos diversos estabelecimentos. Temos uma lista simples, mas que nos permite começar a brincar com a busca desse aplicativo.

Visão geral do projeto

Vamos passar aqui pelas principais classes, caso você ainda não conheça o projeto. Na lateral esquerda da interface, temos as pastas principais. Em ChefDelivery > App > ContentView, temos a tela inicial que monta os elementos da Home. Temos a pasta Model, que são as classes que utilizamos para montar os objetos na tela. Principalmente o StoreType, que é o estabelecimento que usaremos.

Temos a pasta de Views, composta por outras subpastas como Home, NavigationBar, GridView, CarouselView, StoreTypes, e a SearchStoreView que usaremos bastante, que corresponde àquela lista que mostramos anteriormente.

Por fim, temos duas classes importantes, que são de Networking, de requisição de rede, que faz a chamada para a API. Como não temos um servidor dedicado para trazer as informações de requisição, estamos simulando a resposta de um endpoint (ponto final) em uma ferramenta chamada Apiary, que mostrarei para você.

Dentro da pasta Networking, temos a classe HomeService, que traz os elementos da Home, principalmente as lojas listadas na parte inferior do aplicativo. E temos uma outra classe, a SearchService. Temos aqui uma chamada que trará a lista de restaurantes.

Configurando o Apiary

É importante que você configure o seu próprio Apiary, porque o link que usamos dentro do arquivo SearchService é pessoal, ou seja, é da Alura, e em algum momento podemos editar, e aí pode quebrar o código caso você não tenha ainda feito seu próprio Apiary.

Deixaremos nessa sessão do curso o JSON que você utilizará para colocar no seu próprio Apiary. Temos aqui o de Search (Busca) e o da Home.

Basta copiar todo esse JSON com esses parâmetros que mostrarei para você na API, mas basicamente você terá que selecionar todo o JSON, usar o atalho Command + C ou Ctrl + C, e aí você abrirá a API no navegador.

Primeiro, você entrará no endereço app.apiary.io, fará seu login usando sua conta do GitHub, e cairá em uma tela dividida em duas metades. No nosso caso, já inserimos o JSON tanto da Home quanto do Search, e é basicamente o que você terá que fazer.

Você terá que configurar um path (caminho), nesse caso configuramos o /search, configurar o método da requisição na linha 244, que é um GET ### Search [GET], e configurar o Response (resposta), com status code 200, com application/json na linha 246.

## Search [/search]

### Search [GET]

+ Response 200 (application/json)

Se você não configurar corretamente, não funcionará: preste atenção ao espaçamento que você tem que deixar entre o início do JSON que você usará.

Atenção: sempre que for colar seu JSON, você pressionará a tecla Tab duas vezes e depois cole o código copiado usando o atalho Command + V ou Ctrl + V.

Depois que salvar, clicando nesse botão azul Save na parte superior direita, ele trará o endereço que você poderá utilizar no aplicativo.

Após colar o JSON na Apiary, você vai clicar no botão Search, na barra lateral direita. Ele fornecerá então o endereço. É importante que a opção Mock Server esteja selecionada, no campo Request.

Você vai copiar o link, retornar ao projeto, abrir o arquivo SearchService, por exemplo, e vai colar o endereço.

struct SearchService {
    
    func fetchData() async throws -> Result<[StoreType], RequestError> {
        guard let url = URL(string: "https://private-11274d-chefdeliveryapi.apiary-mock.com/search") else {
            return .failure(.invalidURL)
        }

Então, você vai executar o projeto e testar para verificar se realmente está trazendo a busca no seu aplicativo.

No entanto, é crucial que você tenha configurado o seu próprio Apiary. Dessa forma, você também pode fazer as personalizações futuras que precisar.

Portanto, o objetivo deste vídeo foi mostrar o projeto, como configurar o Apiary e, em seguida, vamos começar a pensar efetivamente no escopo das funcionalidades que precisaremos implementar.

Funcionalidade de busca - Desenvolvendo a view da funcionalidade de pesquisa

Agora que já configuramos o Apiary, tanto da Home quanto do Search, é hora de conhecermos nossa primeira tarefa.

Com o simulador aberto na aba de busca, a ideia é otimizar a busca desses restaurantes e estabelecimentos. Reparem que, atualmente, quando precisamos buscar um restaurante, não temos como inserir um Search. Se quisermos buscar "churrasco", por exemplo, teríamos que percorrer a lista até encontrar a loja ou estabelecimento desejado.

Implementando a barra de busca

A primeira funcionalidade será justamente essa: adicionar um Search, que é a barra de busca que colocamos no início da lista, para otimizar essa busca.

Temos aqui uma lista do que precisamos fazer, com algumas etapas.

Primeiro, precisamos adicionar um Search Text na View. Depois de colocar esse Search na View, precisamos desenvolver a lógica para fazer o filtro, que é filtrar os restaurantes, considerando o valor que a pessoa usuária digita no campo de busca. Se ela digitar "churrasco", por exemplo, precisamos filtrar exatamente essa loja e recarregar a lista mostrando os resultados.

A novidade, onde começamos a entrar no tema do curso, é estabelecer o critério de aceite que temos para essa tarefa, que é construir ou desenvolver os testes de unidade. Esse é um dos pontos importantes para que possamos entregar essa tarefa.

Então, vamos lá. Vamos começar criando a View do Search. Vamos abrir o arquivo SearchStoreView. Temos a estrutura básica que mostra toda essa lista.

Primeiro, precisamos criar o Search. Temos na linha 15 um marcador de View: // MARK: - Views. Logo abaixo desse marcador, vamos começar a desenvolver esse Search.

Na linha 17, vamos escrever var searchTextView. E o tipo dela é View. Dentro dela, vamos colocar um Horizontal Stack View. Lembrando, porque vamos ter o campo de texto e um botão que é o de apagar o texto digitado. Como precisamos colocar um elemento ao lado do outro, vamos usar um HStack.

Dentro dele, vamos ter um TextField, que é a barra de busca. Aqui podemos passar dois parâmetros. O primeiro parâmetro é um título, como se fosse um placeholder (espaço reservado). Assim, a pessoa usuária bate o olho naquele campo de texto, ela sabe que pode digitar ali.

Vamos digitar, por exemplo, um placeholder com "Pesquisar em Chef Delivery". E o segundo parâmetro é uma string com o tipo Binding. Esse tipo é uma variável que vamos ter que criar, que vai ser a variável que vai captar os dígitos que a pessoa usuária está fazendo na busca. Vamos fazer isso logo acima do marcador, abaixo da linha 13.

Vamos declarar uma variável do tipo @State, porque ela vai ficar sendo observada a todo instante, a cada clique que a pessoa usuária fizer no campo de texto.

Então, vai ser um var. Vamos chamar de searchText do tipo string. E vamos inicializar essa variável como uma string vazia. Vamos copiar o título desse search e vamos colar no segundo parâmetro.

A ideia principal é essa. Agora, vamos usar alguns modificadores para alterar o tipo do tamanho. Como ela é um Binding, vamos ter que colocar o símbolo $ na frente de searchText, resultando em $searchText.

Vamos customizar esse TextField. Vamos colocar um padding de 7, depois vamos colocar um padding horizontal. Lembrando que, quando coloco um padding horizontal, ele pega tanto a margem esquerda quanto a margem direita. Vamos colocar o valor de 25 para o padding horizontal.

Vamos colocar uma cor de fundo. Vai ser uma cor do próprio sistema, Color(.systemGray6). Logo abaixo, vou colocar um arredondamento no valor de 8 com o cornerRadius.

import SwiftUI

struct SearchStoreView: View {
    
    @ObservedObject var viewModel: SearchStoreViewModel
    
    // MARK: - Views
    
    var searchTextView: some View {
        HStack {
            TextField("Pesquisar em Chef Delivery", text: $viewModel.searchText)
                .padding(7)
                .padding(.horizontal, 25)
                .background(Color(.systemGray6))
                .cornerRadius(8)

A ideia agora é criarmos o botão que vai ao lado desse search, que vai ser o botão para apagar o texto digitado. O botão é um Button mesmo, onde tem aqui uma action e uma label.

Se eu der um Enter, ele vai abrir a estrutura do método na linha 27. Então, quando clicarmos nesse botão, ele vai limpar o search. Para fazer isso, temos que pegar a variável que estamos usando para gerenciar o texto que a pessoa usuária digitou, e vamos atribuir o valor de string vazio.

Em label, podemos colocar uma imagem, como se fosse um ícone para esse botão. Vamos pegar uma imagem do próprio sistema. E abaixo, vamos fazer algumas customizações, como, por exemplo, mudar a cor dele para cinza, e também acrescentar um padding do lado direito. Então, é trailing com valor de 8. Por último, vamos colocar um padding na linha 34, com valor para margem superior de 8.

// Trecho de código suprimido

            Button {
                viewModel.searchText = ""
            } label: {
                Image(systemName: "xmark.circle.fill")
                    .foregroundColor(.gray)
                    .padding(.trailing, 8)
            }
        }
        .padding(.top, 8)
    }

    var body: some View {
        NavigationView {
            VStack {
                List {
                    searchTextView

Onde vamos usar esse search? Temos aqui na linha 40 a lista, e a ideia é colocar ele dentro da lista, mas antes do for, na linha 42. Vamos chamar o searchTextView, e rodar o projeto.

Vamos rodar o simulador. Ele vai subir o projeto. Vamos clicar na barra de busca. Temos aqui o search, embora ele ainda não esteja fazendo a filtragem.

Se digitarmos, por exemplo, "food", não acontece nada com a lista, mas já temos aqui a estrutura do search. Inclusive, o botão também já está funcionando. Quando clicamos no "X" na lateral direita da barra de pesquisa, ele apaga o que foi digitado no campo de busca.

A única coisa que vamos fazer para finalizar é tirar essa linha que está na parte superior do search. Vamos colocar mais um modificador para esse search.

Na linha onde estamos chamando o search, vamos dar um ponto e chamar esse listRowSeparator. E aqui passo um hidden.

    var body: some View {
        NavigationView {
            VStack {
                List {
                    searchTextView
                        .listRowSeparator(.hidden)

Vamos testar. Vamos subir de novo o simulador, clicar em busca. E temos, então, a primeira parte do search. Primeira etapa está feita. Construímos já a view. Próximo passo é fazer esse search funcionar.

Sobre o curso iOS: melhorando o app com testes de unidade e TDD

O curso iOS: melhorando o app com testes de unidade e TDD possui 138 minutos de vídeos, em um total de 52 atividades. Gostou? Conheça nossos outros cursos de iOS em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda iOS acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas