SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
15.11.2024

Lição: 030: Testes e Depuração de Programas Solana

Nesta aula, vamos explorar como testar e depurar efetivamente programas Solana escritos em Rust. Os testes são uma parte crítica do processo de desenvolvimento, garantindo que seu programa funcione como pretendido e se comporte corretamente sob diversas condições. Vamos abordar tanto testes unitários quanto testes de integração, além de discutir técnicas e ferramentas de depuração disponíveis no ecossistema Solana.

Configurando Seu Ambiente

Antes de começarmos a escrever testes, certifique-se de que você tenha um ambiente de desenvolvimento Solana configurado. Você deve ter o seguinte:

  • Rust instalado (use rustup)
  • Solana CLI para interagir com a blockchain
  • Node.js e npm para dependências do framework de testes

Certifique-se de que seu Solana CLI está configurado para o devnet:

solana config set --url devnet

Escrevendo Testes Unitários

Os testes unitários em Rust podem ser definidos no mesmo arquivo que o código do seu programa. Você pode aproveitar o framework de testes embutido do Rust para criar e executar seus testes.

Aqui está um exemplo simples de um programa Solana com testes unitários. Vamos criar um programa que incrementa um número armazenado em uma conta:

Passo 1: Crie Seu Programa

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWxTWqDkWvSmtWvLp9mcGkKAcXes");

#[program]
pub mod contador {
    use super::*;

    pub fn inicializar(ctx: Context<Inicializar>) -> ProgramResult {
        let conta = &mut ctx.accounts.conta_contador;
        conta.contador = 0;
        Ok(())
    }

    pub fn incrementar(ctx: Context>Incrementar>) -> ProgramResult {
        let conta = &mut ctx.accounts.conta_contador;
        conta.contador += 1;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Inicializar<'info> {
    #[account(init, payer = usuario, space = 8 + 8)]
    pub conta_contador: Account<'info, ContaContador>,
    #[account(mut)]
    pub usuario: Signer<'info>,
    pub sistema_programa: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Incrementar<'info> {
    #[account(mut)]
    pub conta_contador: Account<'info, ContaContador>,
}

#[account]
pub struct ContaContador {
    pub contador: u64,
}

Passo 2: Escreva Testes Unitários

Agora, podemos criar um módulo de teste na parte inferior do mesmo arquivo. Este módulo incluirá testes para as funções inicializar e incrementar.

#[cfg(test)]
mod testes {
    use super::*;
    use anchor_lang::prelude::*;
    use anchor_lang::solana_program::program::invoke_signed;
    use solana_program_test::*;

    #[tokio::test]
    async fn teste_inicializar() {
        let mut teste_programa = ProgramTest::new(
            "contador",
            id(),
            processor!(process_instruction),
        );

        let (mut cliente_bancos, pagador, hash_ultimo_bloco) = teste_programa.start().await;

        let conta_contador = Keypair::new();

        // Inicializa a conta contador
        let tx = Transaction::new_signed_with_payer(
            &[instruction::inicializar(
                accounts::Inicializar {
                    conta_contador: conta_contador.pubkey(),
                    usuario: pagador.pubkey(),
                    sistema_programa: system_program::id(),
                },
            )],
            Some(&pagador.pubkey()),
            &[&pagador, &conta_contador],
            hash_ultimo_bloco,
        );

        // Envia a transação
        cliente_bancos.process_transaction(tx).await.unwrap();

        // Verifica a inicialização
        let dados_conta = cliente_bancos
            .get_account(conta_contador.pubkey())
            .await
            .unwrap()
            .unwrap();

        let conta_contador: ContaContador = try_from_slice(&dados_conta.data).unwrap();
        assert_eq!(conta_contador.contador, 0);
    }

    #[tokio::test]
    async fn teste_incrementar() {
        let mut teste_programa = ProgramTest::new(
            "contador",
            id(),
            processor!(process_instruction),
        );

        let (mut cliente_bancos, pagador, hash_ultimo_bloco) = teste_programa.start().await;

        let conta_contador = Keypair::new();

        // Inicializa a conta contador
        let tx = Transaction::new_signed_with_payer(
            &[instruction::inicializar(
                accounts::Inicializar {
                    conta_contador: conta_contador.pubkey(),
                    usuario: pagador.pubkey(),
                    sistema_programa: system_program::id(),
                },
            )],
            Some(&pagador.pubkey()),
            &[&pagador, &conta_contador],
            hash_ultimo_bloco,
        );

        cliente_bancos.process_transaction(tx).await.unwrap();

        // Incrementa o contador
        let tx = Transaction::new_signed_with_payer(
            &[instruction::incrementar(accounts::Incrementar {
                conta_contador: conta_contador.pubkey(),
            })],
            Some(&pagador.pubkey()),
            &[&pagador, &conta_contador],
            hash_ultimo_bloco,
        );

        cliente_bancos.process_transaction(tx).await.unwrap();

        // Verifica o incremento
        let dados_conta = cliente_bancos
            .get_account(conta_contador.pubkey())
            .await
            .unwrap()
            .unwrap();

        let conta_contador: ContaContador = try_from_slice(&dados_conta.data).unwrap();
        assert_eq!(conta_contador.contador, 1);
    }
}

Executando Testes

Para executar seus testes, simplesmente execute o seguinte comando no seu terminal:

cargo test

Esse comando compilará seu programa e executará todos os testes definidos no arquivo do seu programa.

Depurando Seus Programas Solana

Depurar programas Solana pode ser desafiador devido à sua natureza de execução em um ambiente distribuído. No entanto, aqui estão algumas estratégias e ferramentas que você pode usar para depuração:

1. Registro de Log

Use logs para obter insights sobre o fluxo de execução e o estado do seu programa. Em Rust, você pode usar o macro msg! para imprimir mensagens nos logs do programa:

msg!("Valor do contador antes do incremento: {}", conta.contador);

2. Testando em um Ambiente Local

O framework de teste de programas Solana cria um validador de teste local que simula a rede Solana. Você pode usar esse ambiente local para testar mudanças rapidamente. Inicie o validador de teste local em outra janela do terminal:

solana-test-validator

3. Solana Explorer

Após implantar seu programa em um cluster, use o Solana Explorer para visualizar detalhes e logs das transações. Esta ferramenta fornece uma interface gráfica para explorar transações, estados de conta e logs de eventos.

4. Ferramentas de Depuração

Utilize ferramentas de depuração do Rust:

  • Cargo: Você pode executar seus testes com a opção -- --nocapture para ver logs no terminal.
  • GDB: Use o GDB para depurar binários Rust, mas isso requer configuração adicional e pode ser complexo para iniciantes.

Conclusão

Os testes e a depuração são vitais para desenvolver programas Solana confiáveis. Nesta aula, cobrimos como estruturar seus testes, escrever testes unitários para seus programas Solana e utilizar métodos de depuração. Continue praticando e refinando suas habilidades, e você se tornará proficiente em desenvolver aplicações sólidas na Solana!

Did you like this article? Rate it from 1 to 5:

Thank you for voting!