Primeiras aulas do curso Mocks em Java: Testes de comportamentos automatizados

Mocks em Java: Testes de comportamentos automatizados

Simulando comportamentos com Mock Objects - Introdução à simulação de objetos

Seja bem-vindos ao curso de Mocks da Alura! Aqui, vamos trabalhar sobre um sistema de Leilão. Você poderá fazer o fazer o download do projeto aqui.

Escrever testes de unidade, além de divertido, é parte vital para o desenvolvimento de um software de fácil manutenção e evolução. Durante o curso, trabalharemos em cima de um sistema de leilão. Abaixo, você poderá ver a classe responsável por encerrar os leilões cuja data já expirou.

public class EncerradorDeLeilao {

    private int total = 0;

    public void encerra() {

        LeilaoDao dao = new LeilaoDao();
        List<Leilao> todosLeiloesCorrentes = dao.correntes();

        for(Leilao leilao : todosLeiloesCorrentes) {
            if(comecouSemanaPassada(leilao)) {
                leilao.encerra();
                total++;
                dao.atualiza(leilao);
            }
        }
    }

    private boolean comecouSemanaPassada(Leilao leilao) {
        return diasEntre(leilao.getData(), Calendar.getInstance()) >= 7;
    }

    private int diasEntre(Calendar inicio, Calendar fim) {
        Calendar data = (Calendar) inicio.clone();
        int diasNoIntervalo = 0;
        while (data.before(fim)) {
            data.add(Calendar.DAY_OF_MONTH, 1);
            diasNoIntervalo++;
        }
        return diasNoIntervalo;
    }

    public int getTotalEncerrados() {
        return total;
    }
}

Ela é razoavelmente simples de entender: esse código percorre toda a lista de leilões e, caso o leilão tenha sido iniciado semana passada, ele é encerrado e persistido no banco de dados através do DAO. Nosso DAO é convencional. Ele faz uso de JDBC e envia comandos SQL para o banco.

Pensar em cenários de teste para esse problema não é tão difícil:

Escrever estes testes não deve ser uma tarefa tão difícil. Vamos lá:

public class EncerradorDeLeilaoTest {

    @Test
    public void deveEncerrarLeiloesQueComecaramUmaSemanaAtras() {

        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();
        Leilao leilao2 = new CriadorDeLeilao().para("Geladeira")
            .naData(antiga).constroi();

        // mas como passo os leiloes criados para o EncerradorDeLeilao,
        // já que ele os busca no DAO?

        EncerradorDeLeilao encerrador = new EncerradorDeLeilao();
        encerrador.encerra();

        assertTrue(leilao1.isEncerrado());
        assertTrue(leilao2.isEncerrado());
    }
}

Mas o problema é: como passamos o cenário para a classe EncerradorDeLeilao, já que ela busca esses dados do banco de dados?

Uma solução seria adicionar todos esses leilões no banco de dados, um a um, para cada cenário de teste. Ou seja, faríamos diversos inserts no banco de dados e teríamos o cenário pronto. Neste caso, vamos usar o próprio DAO para inserir os leilões. Além disso, já que o DAO criará novos objetos, precisamos buscar os leilões novamente no banco, para garantir que eles estão encerrados:

    @Test
    public void deveEncerrarLeiloesQueComecaramUmaSemanaAtras() {

        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();
        Leilao leilao2 = new CriadorDeLeilao().para("Geladeira")
            .naData(antiga).constroi();

        LeilaoDao dao = new LeilaoDao();
        dao.salva(leilao1);
        dao.salva(leilao2);

        EncerradorDeLeilao encerrador = new EncerradorDeLeilao(dao);
        encerrador.encerra();

        // busca no banco a lista de encerrados
        List<Leilao> encerrados = dao.encerrados();

        // vamos conferir tambem o tamanho da lista!        
        assertEquals(2, encerrados.size());
        assertTrue(encerrados.get(0).isEncerrado());
        assertTrue(encerrados.get(1).isEncerrado());
    }

Isso parece resolver o nosso problema, afinal, agora o teste passou! Mas isso não é suficiente: se rodarmos o teste uma segunda vez, ele quebrará!

Isso acontece porque como mantivemos o cenário no banco de dados, na segunda execução do teste já existiam leilões lá! Então precisamos lembrar de sempre limpar o cenário antes do início de cada teste, dando um DELETE ou um TRUNCATE TABLE.

Veja quanto trabalho para testar uma simples regra de negócio! Isso sem contar que o teste agora leva muito mais tempo para ser executado, afinal conectamos em um banco de dados todas as vezes que o rodamos!

Precisamos conseguir testar essa classe EncerradorDeLeilao sem depender do banco de dados. A grande pergunta é: como fazer isso?

Uma ideia seria simular o banco de dados. Veja que, para a classe EncerradorDeLeilao, não importa como o DAO faz o serviço dela. O encerrador está apenas interessado em alguém que saiba devolver uma lista de leilões e que saiba persistir um leilão. Como isso é feito para o encerrador pouco importa.

Vamos criar uma classe que finge ser um DAO. Ela persistirá as informações em uma simples lista:

public class LeilaoDaoFalso {

    private static List<Leilao> leiloes = new ArrayList<Leilao>();

    public void salva(Leilao leilao) {
        leiloes.add(leilao);
    }

    public List<Leilao> encerrados() {

        List<Leilao> filtrados = new ArrayList<Leilao>();
        for(Leilao leilao : leiloes) {
            if(leilao.isEncerrado()) filtrados.add(leilao);
        }

        return filtrados;
    }

    public List<Leilao> correntes() {

        List<Leilao> filtrados = new ArrayList<Leilao>();
        for(Leilao leilao : leiloes) {
            if(!leilao.isEncerrado()) filtrados.add(leilao);
        }

        return filtrados;
    }

    public void atualiza(Leilao leilao) { /* faz nada! */ }
}

Agora vamos fazer com que o EncerradorDeLeilaoe o EncerradorDeLeilaoTest usem o DAO falso. Além disso, vamos pegar o total de encerrados agora pelo próprio EncerradorDeLeilao, uma vez que pegar pelo DAO não adianta mais (o DAO é falso!):

public class EncerradorDeLeilaoTest {

    @Test
    public void deveEncerrarLeiloesQueComecaramUmaSemanaAtras() {

        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();
        Leilao leilao2 = new CriadorDeLeilao().para("Geladeira")
            .naData(antiga).constroi();

        // dao falso aqui!
        LeilaoDaoFalso daoFalso = new LeilaoDaoFalso();
        daoFalso.salva(leilao1);
        daoFalso.salva(leilao2);

        EncerradorDeLeilao encerrador = new EncerradorDeLeilao();
        encerrador.encerra();

        assertEquals(2, encerrador.getTotalEncerrados());
        assertTrue(leilao1.isEncerrado());
        assertTrue(leilao2.isEncerrado());
    }
}

public class EncerradorDeLeilao {
    public void encerra() {

        // DAO falso aqui!
        LeilaoDaoFalso dao = new LeilaoDaoFalso();
        List<Leilao> todosLeiloesCorrentes = dao.correntes();

        for(Leilao leilao : todosLeiloesCorrentes) {
            if(comecouSemanaPassada(leilao)) {
                leilao.encerra();
                total++;
                dao.atualiza(leilao);
            }
        }
    }

    // classe continua aqui...
}

Rodamos o teste novamente e pronto! Veja que agora ele rodou rápido! Nosso teste está mais simples, mas ainda assim não é ideal. Sempre que criarmos um método novo no DAO, precisaremos escrevê-lo também no LeilaoDaoFalso. Se precisarmos fazer testes de casos excepcionais como, por exemplo, uma exceção lançada no método salva(), precisaríamos de vários DAOs falsos.

Ou seja, a ideia de objetos falsos é boa; precisamos apenas encontrar uma maneira ideal de implementá-la. Objetos que simulam os comportamentos dos objetos reais é o que chamamos de mock objects ou objetos dublês. Eles são especialmente úteis em testes em que temos objetos que se integram com outros sistemas.

E o melhor: os frameworks de mock objects tornam esse trabalho extremamente simples! Neste curso, estudaremos o Mockito. Ele é um dos frameworks de mock mais populares do mercado.

O primeiro passo é é criar um mock do objeto que queremos simular. Em nosso caso, queremos criar um mock de LeilaoDao:

LeilaoDao daoFalso = mock(LeilaoDao.class);

Veja que fizemos uso do método mock. Esse método deve ser importado estaticamente da classe do próprio Mockito:

import static org.mockito.Mockito.*;

Pronto! Temos um mock criado! Precisamos agora ensiná-lo a se comportar da maneira que esperamos. Vamos ensiná-lo, por exemplo, a devolver a lista de leilões criados quando o método correntes() for invocado:

        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();
        Leilao leilao2 = new CriadorDeLeilao().para("Geladeira")
            .naData(antiga).constroi();

        List<Leilao> leiloesAntigos = Arrays.asList(leilao1, leilao2);

        // criando o mock!
        LeilaoDao daoFalso = mock(LeilaoDao.class);
        // ensinando o mock a reagir da maneira que esperamos!
        when(daoFalso.correntes()).thenReturn(leiloesAntigos);

Veja que o método when(), também do Mockito, recebe o método que queremos simular. Em seguida, o método thenReturn() recebe o que o método falso deve devolver. Veja que simples! Agora, quando invocarmos daoFalso.correntes(), ele devolverá leiloesAntigos!

Levemos esse código agora para nosso método de teste:

    @Test
    public void deveEncerrarLeiloesQueComecaramUmaSemanaAtras() {

        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();
        Leilao leilao2 = new CriadorDeLeilao().para("Geladeira")
            .naData(antiga).constroi();
        List<Leilao> leiloesAntigos = Arrays.asList(leilao1, leilao2);

        // criamos o mock
        LeilaoDao daoFalso = mock(LeilaoDao.class);
        // ensinamos ele a retornar a lista de leiloes antigos
        when(daoFalso.correntes()).thenReturn(leiloesAntigos);

        EncerradorDeLeilao encerrador = new EncerradorDeLeilao();
        encerrador.encerra();

        assertTrue(leilao1.isEncerrado());
        assertTrue(leilao2.isEncerrado());
        assertEquals(2, encerrador.getQuantidadeDeEncerrados());
    }

Mas, ao executar o teste, ele falha! Isso ocorre porque a classe EncerradorDeLeilao não faz uso do mock que criamos. Veja que ela ainda instancia o DAO falso. Precisamos fazer com que o EncerradorDeLeilao receba o mock na hora do teste e receba a classe de verdade quando o sistema estiver em produção.

Uma solução é receber o LeilaoDao no construtor. Nesse caso, o teste passaria o mock para o Encerrador:

public class EncerradorDeLeilao {

    private int encerrados;
    private final LeilaoDao dao;

    public EncerradorDeLeilao(LeilaoDao dao) {
        this.dao = dao;
    }

    public void encerra() {
        List<Leilao> todosLeiloesCorrentes = dao.correntes();

        for(Leilao leilao : todosLeiloesCorrentes) {
            if(comecouSemanaPassada(leilao)) {
                encerrados++;
                leilao.encerra();
                dao.salva(leilao);
            }
        }
    }

    // codigo continua aqui
}

public class EncerradorDeLeilaoTest {

    @Test
    public void deveEncerrarLeiloesQueComecaramUmaSemanaAtras() {

        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();
        Leilao leilao2 = new CriadorDeLeilao().para("Geladeira")
            .naData(antiga).constroi();
        List<Leilao> leiloesAntigos = Arrays.asList(leilao1, leilao2);

        LeilaoDao daoFalso = mock(LeilaoDao.class);
        when(daoFalso.correntes()).thenReturn(leiloesAntigos);

        EncerradorDeLeilao encerrador = new EncerradorDeLeilao(daoFalso);
        encerrador.encerra();

        assertTrue(leilao1.isEncerrado());
        assertTrue(leilao2.isEncerrado());
        assertEquals(2, encerrador.getQuantidadeDeEncerrados());
    }
}

Agora sim! Nosso teste passa! Ao invocar o método encerra(), o DAO que é utilizado é o mock; ele, por sua vez, nos devolve a lista "de mentira", e conseguimos executar o teste!

Perceba como agora foi fácil escrevê-lo, afinal o Mockito facilitou a nossa vida! Conseguimos simular o comportamento do DAO e testar a classe que queríamos sem precisarmos montar cenários no banco de dados!

Mock objects são uma ótima alternativa para facilitar a escrita de testes de unidade para classes que dependem de outras classes!

Garantindo que os métodos foram invocados - Verificando métodos invocados

Agora que conseguimos simular nosso banco de dados, o teste ficou fácil de ser escrito. Mas veja que ainda não testamos o método encerra() da classe EncerradorDeLeilao por completo. Observe o código dele abaixo:

    public void encerra() {
        List<Leilao> todosLeiloesCorrentes = dao.correntes();

        for (Leilao leilao : todosLeiloesCorrentes) {
            if (comecouSemanaPassada(leilao)) {
                leilao.encerra();
                total++;
                dao.atualiza(leilao);
            }
        }
    }

Testamos se os leilões são ou não encerrados, mas ainda não garantimos que os leilões são atualizados pelo DAO após o encerramento. Então, como garantiremos que o método atualiza() foi invocado?

Se não estivéssemos mockando o DAO seria fácil: bastaria fazer um select no banco de dados e verificar se a coluna foi alterada. Porém com o mock precisamos perguntar se o método foi invocado. Para isso, faremos uso do método verify do Mockito indicando qual métodos queremos verificar. Veja o teste abaixo:

    @Test
    public void deveAtualizarLeiloesEncerrados() {

        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();

        RepositorioDeLeiloes daoFalso = mock(RepositorioDeLeiloes.class);
        when(daoFalso.correntes()).thenReturn(Arrays.asList(leilao1));

        EncerradorDeLeilao encerrador = new EncerradorDeLeilao(daoFalso);
        encerrador.encerra();

        // verificando que o metodo atualiza foi realmente invocado!
        verify(daoFalso).atualiza(leilao1);
    }

Note que passamos para o método atualiza() a variável leilao1. O Mockito verificará se o método atualiza() foi invocado com a variável leilao1 sendo passada e caso passemos um outro leilão, o teste falhará.

Nesse momento, nosso teste passa! Por curiosidade, se comentarmos a linha dao.atualiza(leilao); na classe EncerradorDeLeilao, o teste falhará com a seguinte mensagem:

Veja que a mensagem diz que o método não foi invocado. Pronto! Dessa forma conseguimos testar a invocação de métodos.

Podemos melhorar essa verificação ao dizer ao verify() que esse método deve ser executado uma única vez, e que, caso ele seja invocado mais de uma vez, o teste deve falhar:

    @Test
    public void deveAtualizarLeiloesEncerrados() {

        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();

        RepositorioDeLeiloes daoFalso = mock(RepositorioDeLeiloes.class);
        when(daoFalso.correntes()).thenReturn(Arrays.asList(leilao1));

        EncerradorDeLeilao encerrador = new EncerradorDeLeilao(daoFalso);
        encerrador.encerra();

        verify(daoFalso, times(1)).atualiza(leilao1);
    }

Veja o times(1). Ele também é um método da classe Mockito. Ali passamos a quantidade de vezes que o método deve ser invocado; poderia ser duas, três, quatro, ou qualquer outro número. Por curiosidade, se fizermos a classe EncerradorDeLeilao invocar duas vezes o DAO, nosso teste falhará. Ele nos avisará que o método foi invocado duas vezes, o que não é esperado:

Através do verify(), conseguimos, então, testar que métodos são invocados, garantindo o comportamento de uma classe por completo.

Mocks que lançam exceções - Avisando usuários por e-mail

Nossos usuários agora precisam ser avisados por e-mail sobre os leilões encerrados. Para implementar essa funcionalidade, imagine a interface Carteiro:

public interface Carteiro {
    void envia(Leilao leilao);
}

Precisamos que nossa classe EncerradorDeLeilao faça uso dela para enviar o e-mail logo após encerrar o leilão. Para isso, receberemos Carteiro no construtor e o invocaremos logo após persistir no DAO:

public class EncerradorDeLeilao {

    private int total = 0;
    private final RepositorioDeLeiloes dao;
    private final Carteiro carteiro;

    public EncerradorDeLeilao(RepositorioDeLeiloes dao, Carteiro carteiro) {
        this.dao = dao;
        // guardamos o carteiro como atributo da classe
        this.carteiro = carteiro;
    }
    public void encerra() {
        List<Leilao> todosLeiloesCorrentes = dao.correntes();

        for (Leilao leilao : todosLeiloesCorrentes) {
            if (comecouSemanaPassada(leilao)) {
                leilao.encerra();
                total++;
                dao.atualiza(leilao);
                // agora enviamos por email tambem!
                carteiro.envia(leilao);
            }
        }
    }

    // ... codigo continua
}

Nesse momento, provavelmente todos nossos testes devem quebrar. Isso acontecerá pois agora o construtor da classe EncerradorDeLeilao recebe um Carteiro. Então, passe um mock de Carteiro para todos eles. A mudança deve ser parecida com a abaixo:

Carteiro carteiroFalso = mock(Carteiro.class);
EncerradorDeLeilao encerrador = new EncerradorDeLeilao(daoFalso, carteiroFalso);

Nosso método encerra() agora, além de encerrar um leilão, ainda persiste a informação na base de dados e notifica o sistema de envio de emails. Já sabemos que sempre que lidamos com infraestrutura precisamos nos precaver de possíveis problemas que podem acontecer: o banco pode estar fora do ar, o SMTP pode recusar nosso envio de e-mail e assim por diante.

Nosso sistema deve saber se recuperar da falha e continuar para garantir que nosso EncerradorDeLeilao não pare por causa de um erro deste tipo. Para isso, vamos adicionar um try-catch dentro do loop e, caso o DAO lance uma exceção, o encerrador continuará a tratar os próximos leilões da lista. Vamos lá:

    public void encerra() {
        List<Leilao> todosLeiloesCorrentes = dao.correntes();

        for (Leilao leilao : todosLeiloesCorrentes) {
            try {
                if (comecouSemanaPassada(leilao)) {
                    leilao.encerra();
                    total++;
                    dao.atualiza(leilao);
                    carteiro.envia(leilao);
                }
            }
            catch(Exception e) {
                // salvo a excecao no sistema de logs
                // e o loop continua!
            }
        }
    }

Como sempre, vamos garantir que esse comportamento realmente funciona. Sabemos que, se passarmos dois leilões e o DAO lançar uma exceção no primeiro, ainda assim o segundo deve ser processado.

Para simular essa exceção, faremos uso do método doThrow() do Mockito. Esse método recebe um parâmetro: a exceção que deve ser lançada. Em seguida, passamos para ele a mesma instrução when() que já estamos acostumados. Veja o exemplo:

doThrow(new RuntimeException()).when(daoFalso).atualiza(leilao1);

Estamos dizendo ao mock que, quando o método atualiza(leilao1) for invocado no daoFalso, o Mockito deve então lançar a Exception que foi passada para o doThrow.

Vamos ao teste agora:

    @Test
    public void deveContinuarAExecucaoMesmoQuandoDaoFalha() {
        Calendar antiga = Calendar.getInstance();
        antiga.set(1999, 1, 20);

        Leilao leilao1 = new CriadorDeLeilao().para("TV de plasma")
            .naData(antiga).constroi();
        Leilao leilao2 = new CriadorDeLeilao().para("Geladeira")
            .naData(antiga).constroi();

        RepositorioDeLeiloes daoFalso = mock(RepositorioDeLeiloes.class);
        when(daoFalso.correntes()).thenReturn(Arrays.asList(leilao1, leilao2));

        doThrow(new RuntimeException()).when(daoFalso).atualiza(leilao1);

        EnviadorDeEmail carteiroFalso = mock(EnviadorDeEmail.class);
        EncerradorDeLeilao encerrador = 
            new EncerradorDeLeilao(daoFalso, carteiroFalso);

        encerrador.encerra();

        verify(daoFalso).atualiza(leilao2);
        verify(carteiroFalso).envia(leilao2);
    }

Veja que ensinamos nosso mock a lançar uma exceção quando o leilao1 for passado, mas nada deve acontecer com o leilao2. Ao final, verificamos que o DAO e o carteiro receberam leilao2 (afinal, a execução deve continuar). Nosso teste passa!

Se comentarmos o try-catch e rodarmos o teste novamente, ele falhará:

Pronto! Garantimos que nosso sistema continua funcionando mesmo se uma exceção ocorrer! É comum termos tratamentos diferentes dado uma exceção, o Mockito faz com que esses testes sejam fáceis de serem escritos.

Sobre o curso Mocks em Java: Testes de comportamentos automatizados

O curso Mocks em Java: Testes de comportamentos automatizados possui 49 minutos de vídeos, em um total de 34 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.

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

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

  • 1241 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

Premium

  • 1241 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$75
à vista R$900
Matricule-se

Premium Plus

  • 1241 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$100
à vista R$1.200
Matricule-se

Max

  • 1241 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$120
à vista R$1.440
Matricule-se
Procurando planos para empresas?

Acesso por 1 ano

Estude 24h/dia onde e quando quiser

Novos cursos todas as semanas