Baixe o app para aproveitar ainda mais
Prévia do material em texto
Algoritmos e Estruturas de Dados Python e Objetos Universidade Autónoma de Lisboa UAL © 2021 AED: Requisitos 1 / 38 Requisitos Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git UAL © 2021 AED: Requisitos 2 / 38 Requisitos UAL © 2021 AED: Requisitos 3 / 38 Python Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git UAL © 2021 AED: Requisitos 4 / 38 Python Python Python é uma linguagem imperativa, orientada a objetos. Vai ser a linguagem utilizada durante em Algoritmos e Estruturas de Dados. É necessário dominar as seguintes construções da linguagem: • Variáveis • Estruturas de controlo (if, for, while) • Funções • Módulos e pacotes • Exceções • Classes e objetos UAL © 2021 AED: Requisitos 5 / 38 Python Método As aplicações devem ser escritas com um editor de texto, e.g., Visual Studio Code. Se o programa for simples, pode fazer sentido escrever todo o código num só ficheiro. Geralmente, os programas terão vários ficheiros. O código escrito num ficheiro (ou vários) deve ser executado com o interpretador python. Este está disponível na linha de comandos. Se a distribuição Python for a Anaconda, a linha de comandos deve ser a disponibilizada pela Anaconda. UAL © 2021 AED: Requisitos 6 / 38 Python Método (2) A execução de código deve ser feita com recurso ao contexto main. if __name__ == "__main__": # Executar neste contexto É necessário sistematizar o processo de desenvolvimento, identificando a funcionalidade a implementar, e garantir que ficou a funcionar depois de implementada. Vamos recorrer a testes unitários para garantir que os artefactos que produzidos têm o comportamento pretendido. UAL © 2021 AED: Requisitos 7 / 38 Python Metodologia Qualquer programa pode dividir a sua implementação em três tipos de módulo: Módulos de modelos de informação: Contêm as estruturas de dados, e métodos que operam sobre elas diretamente. Módulos de controlo: Contém as regras de negócio, que relacionam os vários modelos de informação. Permitem criar as condições para dar resposta aos pedidos do utilizador. Módulos de interação: Leem as instruções do utilizador, e entregam as respostas. Existem mais abordagens para organizar um programa. Em AED, vamos construir bibliotecas de estruturas e dados e programas. Quando for necessário implementar um programa, vamos funcionar com esta metodologia. UAL © 2021 AED: Requisitos 8 / 38 Python Advertência Todas as funções, módulos, e classes devem ser experimentadas. Não faz sentido entregar código que nunca foi executado com sucesso. É necessário ficar confortável com o editor de texto, e com o processo de escrita de código e execução. Esse conforto exige tempo e repetição. Não é suficiente observar terceiros a desenvolver aplicações sem passar pelo processo de resolução de problemas. Vai gerar uma falsa sensação de competência. Os problemas têm que ser resolvidos. O código tem que ser escrito. É sempre útil procurar ajuda, mas não existe substituto, ou alternativa, para o esforço individual. UAL © 2021 AED: Requisitos 9 / 38 Orientação a objetos Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git UAL © 2021 AED: Requisitos 10 / 38 Orientação a objetos Classes e objetos Existem dois conceitos fundamentais em Programação Orientada a Objetos: Classe e Objeto. Uma classe define um novo tipo de dados. É o artefacto de código que contém a descrição dos dados e algoritmos disponíveis para cada objeto. Um objeto é uma instância de uma classe, i.e., é um elemento do tipo definido pela classe, e pode estar atribuído a uma variável, ou fazer parte de uma coleção (e.g., lista). As classes definem métodos que operam sobre a informação contida nos objetos. Em Python, o primeiro atributo de cada método é uma referência para o objeto através o qual o método é executado. Por convenção, esse atributo chama-se self. Da relação entre o conceito de classe e objeto é importante reter que para um determinado tipo de dados, existe uma classe que o define, e, eventualmente, vários objetos criados de acordo com essa definição. UAL © 2021 AED: Requisitos 11 / 38 Orientação a objetos Construção de classes Uma classe é definida com recurso ao operador class. Cada classe tem um nome e, eventualmente, um método construtor de objetos, __init__. class Pessoa: def __init__(self, nome, data_de_nascimento, nacionalidade): self.nome = nome self.data_de_nascimento = data_de_nascimento self.nacionalidade = nacionalidade Quando um objeto é criado, a classe executa o método __init__. UAL © 2021 AED: Requisitos 12 / 38 Orientação a objetos Atributos e métodos Cada objeto vai ter um conjunto de atributos, i.e., variáveis com dados. Os métodos da classe permitem manipular atributos dos objetos. Todos os métodos podem criar e alterar atributos. Geralmente, no método __init__, existe uma declaração inicial do valor dos atributos. class Pessoa: def __init__(self, nome, data_de_nascimento, nacionalidade): self.nome = nome self.data_de_nascimento = data_de_nascimento self.nacionalidade = nacionalidade def idade(self): hoje = date.today() return int((hoje - self.data_de_nascimento).days / 365) UAL © 2021 AED: Requisitos 13 / 38 Orientação a objetos Utilização de objetos Para construir um objeto do tipo de uma classe, é necessário invocar o método construtor. pessoa = Pessoa('Alice', Date(1990, 1, 1), 'PT') O objeto criado, do tipo Pessoa, encontra-se numa determinada localização de memória. Esssa localização é retornada pelo método construtor, e pode ficar guardada numa variável (no exemplo, pessoa). Diz-se que variável guarda um apontador para o objeto. Para executar um método de um objeto utiliza-se um ponto entre o nome da variável que guarda o apontador para o objeto, e o nome do método. idade = pessoa.idade() UAL © 2021 AED: Requisitos 14 / 38 Orientação a objetos Herança É possível definir heranças entre classes. Uma herança representa uma extensão de um tipo de dados, permitindo expressar construções complexas com facilidade. A herança é definida com recurso à indicação da classe base entre parêntesis, após o nome da classe. class Autor(Pessoa): def __init__(self, nome, data_de_nascimento, nacionalidade): Pessoa.__init__(self, nome, data_de_nascimento, nacionalidade) self.obras = [] def registar_obra(self, obra): self.obras.append(obra) UAL © 2021 AED: Requisitos 15 / 38 Orientação a objetos Herança (2) class Autor(Pessoa): def __init__(self, nome, data_de_nascimento, nacionalidade): Pessoa.__init__(self, nome, data_de_nascimento, nacionalidade) self.obras = [] def registar_obra(self, obra): self.obras.append(obra) Um autor é um objeto do tipo Autor que, por sua vez, é um sub-tipo de Pessoa. autor = Autor('Tokien', date(1892, 1, 3), 'UK') autor.registar_obra("LOTR") Responde a métodos do tipo Autor. print(autor.obras) Também responde a métodos do tipo Pessoa. print(autor.idade()) UAL © 2021 AED: Requisitos 16 / 38 Orientação a objetos Exceções Em Python, uma exceção é um tipo de dados definido por uma classe que estende Exception. class SomeException(Exception): pass A exceção pode ser utilizada onde o tipo de dados estiver disponível. def m(): ... raise SomeException() Para tratar a exceção: try: m() except SomeException as error: print('Ocorreu um erro.') UAL © 2021 AED: Requisitos 17 / 38 Testes unitários Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git UAL © 2021 AED: Requisitos 18 / 38 Testes unitários Problema O código deve ser experimentado. Após a interpretação do problema, planeamento, e implementação, é sempre necessário assegurar que o programa executa de acordo com a expectativa. Com um programa concluído, dependemos da instrumentação de entrada e saída de dados (i.e., I/O) para avaliar se este se comporta corretamente. Faz sentido efetuar testes durante a construçãodo programa mas é importante considerar que alterações futuras no código podem estragar funcionalidades que já estão corretas, i.e., é frequente ser necessário alterar código estável para suportar novidades. É importante aplicar uma estratégia de teste que assegure que uma funcionalidade corretamente implementada continua correta. UAL © 2021 AED: Requisitos 19 / 38 Testes unitários Intuição Intuitivamente, experimentamos os programa através cenários de teste que demonstrem a funcionalidade esperada. Por exemplo: • Podemos executar o programa, e utilizar a interface disponível; • Podemos escrever um módulo com o um ponto de entrada (__main__), onde funções e métodos são executados. Seja qual for a estratégia, é importante assegurar que o programa continua correto no que respeita a todos os aspetos testados. UAL © 2021 AED: Requisitos 20 / 38 Testes unitários Sistematização A sistematização dos testes passa por: • Ser possível descrever um teste de uma funcionalidade; • O teste deve ser determinístico, i.e., o input do cenário deve produzir sempre o mesmo output • O teste deve ser o mais simples possível, isolando pequenos incrementos no projeto • Não conseguimos isolar nada que não esteja numa função ou método • O teste mais simples possível será, necessariamente, focado numa única função ou método • As funções ou métodos são as unidades fundamentais de um programa. • Assegurar que, quando efetuamos um teste a uma nova funcionalidade, efetuamos todos os testes anteriores também. Desta forma asseguramos que a nova funcionalidade não interfere com a correção das anteriores. Procuramos um instrumento que suporte testes unitários. UAL © 2021 AED: Requisitos 21 / 38 unittest Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git UAL © 2021 AED: Requisitos 22 / 38 unittest Plataforma unittest A plataforma (i.e., biblioteca e executáveis) para testes unitários incluída na distribuição Python de referência é a unittest. Foi inspirada pela JUnit em Java, e oferece os mecanismos comuns à maioria das soluções disponíveis para construir estes testes. O princípio geral da unittest dita que os testes são métodos de uma classe que estende a classe TestCase, distribuída pela plataforma. Os testes devem ser independentes, e cristalizam cenários de utilização das funções e métodos do projeto. UAL © 2021 AED: Requisitos 23 / 38 https://docs.python.org/3/library/unittest.html https://junit.org/junit5/ unittest Classe unittest.TestCase A classe TestCase, do módulo unittest representa um contexto de testes. Foi desenhada para ser estendida, recorrendo ao mecanismo de herança de Python. Um programa simples tem apenas um contexto de texto, mas com o aumento de complexidade pode fazer sentido separar os testes em vários contextos. Com orientação a objetos, faz sentido ter um contexto por cada classe. A TestCase disponibiliza quatro métodos para configuração (facultativa) dos testes, e um conjunto de métodos para determinar se o teste foi bem ou mal sucedido. Para configuração existe setUp(), tearDown(), setUpClass(), e tearDownClass(). Estes métodos permitem definir código que executa antes ou depois de cada testes, ou do conjunto de testes. Para determinar o resultado do teste existem vários métodos com o prefixo assert. UAL © 2021 AED: Requisitos 24 / 38 unittest Métodos de teste Um teste corresponde a um método da classe que estende TestCase. Para o teste ser interpretado corretamente, o nome do método deve começar com a palavra test. # example.py import unittest class Example(unittest.TestCase): def test_stuff(self): pass A classe pode ter vários métodos que começam com test, e podem ser todos executados automaticamente pela plataforma de testes. Para executar: python -m unittest example.py UAL © 2021 AED: Requisitos 25 / 38 unittest Ponto de início unittest.main() Os testes também podem ser executados diretamente, sendo necessário definir o ponto de início com a invocação de uma função main() do módulo unittest. # example.py import unittest class Example(unittest.TestCase): def test_stuff(self): pass if __name__ == "__main__": unittest.main() Para executar: >python example.py UAL © 2021 AED: Requisitos 26 / 38 unittest Métodos assert Existem vários métodos disponíveis para avaliar testes em TestCase, que passam a estar disponíveis na nossa classe de testes. Método Descrição assertEqual(a, b) a == b assertNotEqual(a, b) a != b assertTrue(x) bool(x) is True assertFalse(x) bool(x) isFalse assertIs(a, b) a is b assertIsNone(x) x is None assertIn(a, b) a in b assertIsInstance(a, b) isinstance(a, b) UAL © 2021 AED: Requisitos 27 / 38 Exemplo unittest Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git UAL © 2021 AED: Requisitos 28 / 38 Exemplo unittest Problema Consideramos um programa que calcula a raiz quadrada de um número. # util.py def square_root(n): x = 100000 s = n/x while x-s >= 0.05: x = (x+s)/2 s = n/x return x if __name__ == "__main__": print(f"Raiz quadrada de 10: {square_root(10)}") > python util.py Raiz quadrada de 10: 3.1638660565937267 Pretendemos construir testes unitários para a função square_root. UAL © 2021 AED: Requisitos 29 / 38 Exemplo unittest Testes # test_util.py import unittest from util import square_root class TestUtil(unittest.TestCase): def test_square_root(self): self.assertEqual(round(square_root(10), 2), 3.16) if __name__ == '__main__': unittest.main() > python test_util.py . --------------------------------------------------------------------- Ran 1 test in 0.000s OK UAL © 2021 AED: Requisitos 30 / 38 Exemplo unittest Mais funcionalidade ⇒ Mais testes Pretendemos agora suportar mais uma operação matemática: o maior divisor comum. Idealmente, começamos por descrever o teste. # test_util.py import unittest from util import square_root class TestUtil(unittest.TestCase): ... def test_mdc(self): a = 12 b = 9 self.assertEqual(mdc(a, b), 3) UAL © 2021 AED: Requisitos 31 / 38 Exemplo unittest Falhar o teste em primeiro lugar > python test_util.py E. ====================================================================== ERROR: test_mdc (__main__.TestUtil) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_util.py", line 12, in test_mdc self.assertEqual(mdc(a, b), 3) NameError: name 'mdc' is not defined ---------------------------------------------------------------------- Ran 2 tests in 0.000s FAILED (errors=1) Falta importar implementar a função mdc de util, e importar nos testes. UAL © 2021 AED: Requisitos 32 / 38 Exemplo unittest Passar o teste depois de falhar # util.py ... def mdc(x, y): c = x - y while c > 0: if c > y: x = c else: x = y y = c c = x - y return x ... > python test_util.py .. ------------------------- Ran 2 tests in 0.000s OK UAL © 2021 AED: Requisitos 33 / 38 Cobertura de testes Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git UAL © 2021 AED: Requisitos 34 / 38 Cobertura de testes Problema Com alguns testes construidos, como determinamos que estes representam realmente todas as funcionalidades implementadas? Independentemente da qualidade dos testes escritos, é possível quantificar a porção de código que está a ser testado pelos vários métodos de teste: basta registar todas as linhas de códigos ativadas durante a execução do teste. Essa medida tem o nome de cobertura de código, e é facilmente calculada com recurso ao módulo coverage. UAL © 2021 AED: Requisitos 35 / 38 Cobertura de testes coverage Este módulo tem que ser instalado no sistema. Para instalar um módulo é necessário recorrer a ferramentas disponíveis no sistema operativo. Com Anaconda: > conda install coverage Sem Anaconda: > pip install coverage Para executar os testes com medição de cobertura: > coverage run --source=. -m unittest discover > coveragereport -m Name Stmts Miss Cover Missing -------------------------------------------- test_util.py 11 1 91% 15 util.py 19 2 89% 22-23 -------------------------------------------- TOTAL 30 3 90% > coverage html UAL © 2021 AED: Requisitos 36 / 38 git Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git UAL © 2021 AED: Requisitos 37 / 38 git git O sistema de controlo de versões git será outra ferramenta fundamental para a realização dos laboratórios e projeto. Os enunciados serão distribuídos através do GitHub Classroom, pelo que será necessário registar uma conta no GitHub. Estará disponível, no e-learning, um registo de contas GitHub, necessário para associar cada número de aluno ao utilizador GitHub respetivo. UAL © 2021 AED: Requisitos 38 / 38 https://github.com/ Python Orientação a objetos Testes unitários unittest Exemplo unittest Cobertura de testes git
Compartilhar