Testador Automático e Método de Avaliação de Programas em Java Ingrid Oliveira de Nunes1 Maria Lúcia Blanck Lisbôa2 exercícios adicionais. Visto que, em geral, elas Abstract To learn a programming não são cobradas pelos professores, muitas dos language implies that students make a lot of alunos não costumam fazê-las. programming exercises. Evaluating these Isso ocorre devido ao fato de ser quase exercises is something hard to be done: in a impossível para o professor avaliar essas listas Object-Oriented language, every class must de exercícios. Uma turma da disciplina be submitted to a lot of tests and, for each one Algoritmos e Programação de uma universidade of them, the results must be checked. This geralmente tem cerca de quarenta alunos. work presents a tool that automatizes the test Submeter os programas de todos os alunos a and the evaluation of programs done in the testes de avaliação torna-se uma tarefa Java language. Keywords programming language, impraticável. Além disso, no caso de linguagens learning, correctness, evaluation, Java. orientadas a objetos, um exercício envolve na Resumo O aprendizado de uma implementação de várias classes e cada uma deveria ser corrigida separadamente. linguagem de programação requer que alunos No presente artigo, apresenta-se uma façam muitos exercícios de programação. A ferramenta desenvolvida em Java que tem por avaliação desses exercícios é feita de forma objetivo resolver este problema. A ferramenta muito trabalhosa: no caso de uma linguagem orientada a objetos, requer que cada classe possibilita uma correção automática de classes implementadas em Java, bem como apresenta seja submetida a uma série de casos de teste e, uma metodologia para a atribuição de notas às para cada um deles, a correção dos resultados classes avaliadas. deve ser verificada. Este trabalho tem por objetivo apresentar uma ferramenta que automatiza o teste e a avaliação de programas 2. Testador Automático feitos na linguagem Java. O Testador Automático de Classes Palavras Chave linguagem de consiste em uma aplicação Java que testa classes programação, aprendizado, correção, avaliação, implementadas nessa linguagem de Java. programação. Ele executa testes presentes em 1. Introdução Segundo Piaget[2], a aprendizagem verdadeira é aquela que faz parte da experiência de vida dos participantes no processo. Durante aprendizado de uma linguagem de programação, vê-se claramente a importância das atividades práticas que o aluno deve ter. Cada tópico que é ensinado nas aulas de programação deveria ter uma aula prática correspondente, na qual os alunos pudessem testar aquilo que viram. O ideal seria que o professor acompanhasse as atividades práticas e avaliasse os programas dados como exercícios. Muitas vezes, a freqüência dessas aulas práticas não é suficiente para o bom aprendizado do aluno. Normalmente, são das listas de uma classe de teste fornecida e verifica a correção de conjuntos de classes que possuem uma determinada estrutura com base em um conjunto-modelo. A estrutura dessas classes necessita seguir um determinado padrão especificado a seguir: a) Pacote Principal deve existir um pacote principal para cada problema de programação, que contém os elementos de teste: a classe de teste, as classes auxiliares e os pacotes do professor e dos alunos. Dentro desse pacote, a classe de teste deve estar presente, bem como todas as classes que são utilizadas no programa e não devem ser testadas. Na prática, elas serão as classes que um professor fornece ______________________________________ 1 Ingrid Oliveira de Nunes, Universidade Federal do Rio Grande do Sul (UFRGS), Instituto de Informática, Porto Alegre, Brasil, 91501-970, [email protected] 2 Maria Lúcia Blanck Lisbôa, Universidade Federal do Rio Grande do Sul (UFRGS), Instituto de Informática, Porto Alegre, Brasil, 91501-970, [email protected] aos alunos e eles devem fazer uso dela e não implementá-las. b) Pacote do Professor este é o pacote que deve conter todas as classes que servirão como modelo para a aferição das classes de igual funcionalidade dos programas dos alunos. As classes presentes nesse pacote servirão como fonte para a criação de interfaces, além disso, o resultado de seus métodos será considerado como correto. O pacote deve encontrar-se dentro do pacote principal. c) Pacotes dos Alunos dentro do pacote principal, deve haver outros pacotes que serão os pacotes dos alunos. Eles devem conter classes com mesmo nome das classes presentes no pacote do professor, bem como implementar construtores e métodos de mesma assinatura que as classes do professor. Não é necessária a criação de uma interface e que os alunos a implementem, visto que isto seria muito avançado para alunos principiantes. Mas sim, o professor deve fornecer um esqueleto da classe a ser implementada. Tendo-se o pacote estruturado da maneira descrita, o testador automático de classes pode ser utilizado para a avaliação dos programas dos alunos, classe a classe, identificando com bastante precisão os erros encontrados durante o processo: problemas com construtores e métodos. 2.1. Gerando Classes Teste e do Professor de A solução encontrada para corrigir as classes sem ser necessário alterar o código desenvolvido pelos alunos foi interceptar a criação e a invocação dos métodos da classe a ser testada, verificando a existência de erros e o retorno após a execução dos mesmos. Para isso, foram criadas classes que implementam a interface InvocationHandler do pacote java.lang.reflect. Ela serve exatamente para fazer essa interceptação. O problema é que InvocationHandler atua sobre um objeto proxy e intercepta os métodos das interfaces públicas da classe do objeto, o que implica a necessidade de todos os programas seguirem exatamente as mesmas interfaces determinadas pelo ´esqueleto´ fornecido pelo professor. A geração das classes de testes consiste na criação de um pacote chamado tester localizado dentro do pacote principal. Seu conteúdo serão pacotes com os nomes das classes a serem testadas. Cada um desses pacotes terá: Uma classe chamada com o nome da classe a ser testada concatenado com o nome da classe de teste. Esta será a classe de teste. Um pacote chamado temp que conterá todas as classes que não a alvo do teste, bem como a interface extraída a partir da classe do professor. Um pacote chamado teacher contendo a classe a ser testada. Nas classes dos dois primeiros itens, todas as instanciações da classe a ser testada serão substituídas pela instanciação de um objeto proxy que tem a invocação dos construtores da classe a ser testada interceptada. Este objeto, por sua vez, fará a instanciação de outro objeto proxy e este será responsável por interceptar a invocação dos métodos da classe. Além disso, será adicionada a importação do pacote principal em todas as classes para caso elas façam uso de classes fornecidas pelo professor. Obtendo-se esta estrutura de classes, é possível iniciar o teste das classes dos alunos. 2.2. Alunos Testando Classes de Tendo-se a estrutura de pacotes, classes e interfaces prontas para a execução dos testes, basta então rodá-los. Ao iniciar a execução de um teste, o programa faz algumas modificações no código do aluno. Basicamente, coloca a classe a ser testada no pacote tester.nomedaclasse.student e acrescenta o código necessário para a classe implementar a interface gerada a partir da classe do professor. Em seguida, é feita a compilação da classe. Nesta etapa, podem aparecer erros, seja por causa de erros de programação do aluno, seja por causa da não implementação correta da interface. Caso tudo ocorra corretamente, a classe de teste iniciará sua execução. Cada construtor e método da classe a ser testada será interceptado ao ser chamado e será executado tanto para a classe do professor como para a classe do aluno. Considerando-se correta a implementação da classe do professor, erros na classe do aluno serão computados. Esse processo é feito com cada uma das classes a ser testada e, ao final, temos a relação número de testes e número de erros de cada uma das classes, possibilitando a atribuição de uma nota à classe testada. 3. Método de Avaliação A correção de uma determinada classe pode ser vista de duas maneiras: Correção Funcional: quando construtores e métodos são executados corretamente e os métodos retornam valores coerentes com o que é esperado. Correção de Estado: quando os campos privativos ou não do objeto representam corretamente o estado que ele se encontra. Neste trabalho, optou-se por efetuar uma correção funcional das classes, permitindo-se ao aluno uma maior flexibilidade na implementação de sua classe, Em [4], é feita uma verificação de cada campo do objeto sendo testado após a execução de cada método. Isso implica que o aluno não tem a liberdade de optar campos deverão fazer parte de sua classe. Durante os testes, são verificados três tipos de erros, relacionados a seguir. 3.1. Erro de Compilação Ocorre quando a classe do aluno não compila por erro de programação, ou por não implementar a interface (esqueleto da classe fornecido pelo professor) ou pela inexistência da classe. A classes com erros deste tipo, são atribuídas notas zero (0). 3.2. Ocorrência de Exceções Ocorre após a instanciação de um objeto ou a execução de um método. São consideradas como erro apenas as execuções que ocorrem com a classe do aluno e que não são declaradas na assinatura do construtor ou do método. No caso da ocorrência da exceção também na classe do professor, a exceção não é contabilizada como erro. 3.3. Incorreto Retorno de Método Ocorre após a execução de um método que retorne algo que não seja void. O retorno do professor é comparado com o do aluno. Caso eles difiram, o erro é contabilizado. 4. Interface Gráfica O uso do Testador Automático de Classes é realizado por meio de interfaces gráficas, que simplificam os passos de avaliação. O primeiro passo é abrir o pacote que se deseja testar, selecionando-se o diretório correspondente. Pode-se visualizar a estrutura do pacote aberto ao lado esquerdo da janela do testador. É possível abrir as classes a fim de ver os seu código dando-se um duplo clique sobre a classe a ser analisada. Ao solicitar a execução de um teste, deve-se selecionar a classe de teste e o pacote do professor. A princípio, todas as classes testadas terão peso um (1) no cálculo da nota final, mas isso pode ser alterado clicando-se no botão Peso das Notas... e atribuindo-se os valores desejados. Feito isso, basta clicar no botão Executar Teste . Após a execução de testes de todas as classes de todos os alunos, um relatório será exibido com as notas parciais e finais de todos os alunos. Este relatório pode ser salvo como um arquivo texto. 5. Estudo de Caso Aqui, será apresentado um exemplo do uso de Testador Automático de Classes. Um professor deseja que seus alunos implementem duas classes: a classe Pessoa e a Classe Aluno. Para sua implementação o aluno deve fazer o uso da classe Data, fornecida pelo professor, e a classe que irá testar as duas classes será a classe ClasseDeTeste. O professor deve fornecer o esqueleto das classes a seus alunos, como exemplificado na figura 1. Figura 1 Cada aluno deverá implementar a sua versão das classes solicitadas. Uma estrutura de classes deverá ser montada, como pode ser visto na figura 2. Tendo os testes executados com sucesso, cada pacote de cada aluno conterá um arquivo texto com o relatório de teste de cada classe e uma janela será exibida com o relatório de teste do pacote (figura 6), o qual pode ser salvo como um arquivo de texto. Figura 2 Tendo isso feito, basta executar os testes. O professor deve solicitar a execução clicando no item Executar Teste encontrado menu arquivo. A seguir, uma a caixa de diálogo será exibida, como é mostrado na figura 3. Figura 3 Aqui, o professor escolhe qual será a classe de teste e qual será o pacote modelo (do professor), bem como configurar os pesos das notas das classes para o cálculo da nota final. A figura 4 é caixa de diálogo onde se realiza esta configuração. Figura 4 Clicando em Executar Teste, os testes serão iniciados. Pode-se acompanhar o andamento da execução através de um caixa de diálogo como a da figura 5. Figura 5 Figura 6 6. Observações No processo de desenvolvimento e correção das classes, alguns aspectos devem ser analisados: O primeiro passo para a correção das classes é a geração das classes de teste e do professor. Caso elas não compilem, o teste é abortado. As classes do professor devem sobrepor o método equals herdado da classe Object do pacote java.lang. Isso permite uma comparação do retorno dos métodos. As classes testadas não podem conter métodos que recebem como parâmetro ou retornam objetos dela mesma. Caso o método toString herdado da classe Object do pacote java.lang seja sobreposto, deve-se ter o cuidado para manter um padrão do string de retorno, pois eles serão comparados letra a letra. Semântica igual e sintaxe diferente para esses strings implicam num retorno considerado incorreto do método. Mesmo que o método toString não seja invocado explicitamente na classe de teste, ele pode ser chamado indiretamente, como, por exemplo, ao chamar o método System.out.println. Caso ocorra algum erro de execução na classe de teste devido a alguma classe do professor, o teste da classe será interrompido e será atribuída a nota máxima à classe testada. Uma notificação no relatório será exibida. 7. Conclusões Perspectivas Futuras e Este trabalho reflete a preocupação de fazer com que alunos que estejam aprendendo uma linguagem de programação tenham atividades práticas em paralelo a o que aprendem nas aulas teóricas. A importância da implementação é fundamental para a fixação do uso dos comandos de controle, dos recursos da linguagem e o desenvolvimento do raciocínio. O professor deve sempre acompanhar a execução dessas atividades práticas. Para tornar esse acompanhamento viável desenvolveu-se uma ferramenta que automatiza tanto a correção como a avaliação de exercícios de programação na linguagem Java. Esta ferramenta, faz projeto JEduc[1], o qual já possui um ambiente de programação em Java simplificado, ideal para quem está aprendendo a linguagem. Além disso, possui uma ferramenta que constrói automaticamente classes de teste[8][9]. Futuramente, deseja-se integrar essa ferramenta ao testador automático de forma que a criação da classe de teste utilizada no testador também seja gerada automaticamente. Deseja-se, também, disponibilizar o uso dessas ferramentas no próprio IDE do JEduc. 8. Referências [1] Brugnara, T., Bertagnolli, S. C., Lisboa, M. L. B., Perego, C. A. JEduc: uma ferramenta livre para auxiliar o ensino da linguagem de programação Java. WORKSHOP SOFTWARE LIVRE, (WSL 2002), 2002, Porto Alegre. [2] Construtivismo em Piaget. http://www.comp.ufla.br/~kacilene/educacao/pia get.html, agosto 2001. [3] Goldwasser, M. H. A Gimmick to Integrate Software Testing Throughout the Curriculum. In: ACM SIGCSE Bulletin. March 2002. [4] Hitchner, L. E. An Automatic Testing and Grading Method for a C++ List Class. In: ACM SIGCSE Bulletin. June 1999 [5] Jones, E. L. Integrating Testing into the Curriculum Arsenic in Small Doses. In: ACM SIGCSE Bulletin, Proceedings of the thirty-second SIGCSE technical symposium on Computer Science. February 2001 [6] Jones, E. L. Grading Student Programs a Software Testing. In: Grading Student Programs a Software Testing. November 2000 [7] Kay, D. G. et al. Automated Grading Assistance For Student Programs. In: ACM SIGCSE Bulletin , Proceedings of the twentyfifth SIGCSE symposium on Computer science education. March 1994 [8] Lima, Márcia J. Geração Automática de Classes Testadoras em Java. Poster - Salão de Iniciação Científica - UFRGS, Novembro de 2003 [9] Lima, Márcia J. Geração Automática de Classes Testadoras em Java. Poster - Jornadas de Iniciação Científica - SBPC, Cuiabá, julho de 2004 [10] Sun Microsystems. www.sun.com, junho 2003.