• Nenhum resultado encontrado

Passo 5: Repetir

2.7 Programação Orientada a Aspectos

2.7.2 Aspectj

Aspectj é uma extensão orientada a aspectos da linguagem Java. É a linguagem orien-

tada a aspectos mais popular, além de ser a primeira a ser proposta. Já foi empregada

em vários estudos sobre orientação a aspectos. Neste trabalho utilizamos Aspectj para

monitorar as exceções que trafegam no decorrer da execução do sistema e forçar o seu

lançamento quando o desenvolvedor considerar isso necessário.

Sintaxe básica do Aspectj

POINTCUTS: Pointcuts são construções de AspectJ que permitem a seleção de um con-

junto de pontos de junção. Pointcuts também permitem que o contexto relativo aos

pontos de junção selecionados seja exposto para os advice associados a eles. Com a uti-

lização de pontos de junção, é possível obter argumentos de métodos, exceções lançadas

no ponto de junção e objetos usados como alvo para chamadas de métodos. Os pontos

de junção selecionados podem ser combinados através dos operadores de intersecção de

conjuntos (&&), união de conjuntos (||) ou negação(!). Para a definição dos pointcuts,

podemos utilizar, por exemplo, os seguintes designadores da tabela

2.7.2. Podemos uti-

lizar wildcards para representar tipos de retorno, nomes ou parâmetros associados a um

conjunto de pontos de junção selecionado. Os seguintes caracteres definem esses wild-

cards: o ” * ” indica qualquer tipo de elemento, ” .. ” aparece em listas de parâmetros e

indica qualquer seqüência independentemente do tipo de objeto, e ”+ ” é utilizado depois

do nome de um tipo para indicar que os seus subtipos também estão sendo selecionados.

Alguns exemplos de pointcuts são apresentados a seguir.

pointcut callexecutionGeneral(): handler(Throwable+)

&& !within(br.cin.ufpe.junite.*);

2.7. PROGRAMAÇÃO ORIENTADA A ASPECTOS

Categoria do Ponto de Junção

Sintaxe

Execução de método

execution(MethodSignature)

Chamada a método

call(MethodSignature)

Execução do construtor

execution(ConstructorSignature)

Chamada ao construtor

call(ConstructorSignature)

Inicialização da classe

staticinitialization(TypeSignature)

Acesso ao atributo

get(FieldSignature)

Atribuição a um atributo

set(FieldSignature)

Execução de tratador de

exceções handler(TypeSignature)

Inicialização de objetos

initialization(ConstructorSignature)

Pré-inicialização de Objetos

preinitialization(ConstructorSignature)

Execução do advice

adviceexecution()

Tabela 2.1 Desiginadores de ponto de junção [45] pg.74

pointcut tc(): execution( void

atunes.kernel.utils.PictureExporter.doExport(..));

No primeiro exemplo, o pointcut intercepta o tratamento de qualquer exceção do tipo

Throwable ou subtipo que não esteja em nenhuma classe do pacote

br.cin.ufpe.junite. Esse pointcut também captura o argumento passado para

o método (args(s)), neste caso a exceção tratada, permitindo que seja exposto posterior-

mente aos advice. No segundo exemplo, são capturadas chamadas ao método doExport

() da classe PictureExporter que não devolve nenhum resultado e tem zero ou

mais parâmetro de qualquer tipo.

Advice

Advice são estruturas que permitem que o código seja associado a um conjunto de pontos

de junção selecionados. O Advice pode usar informação contextual relativa aos pontos

de junção selecionados, se esta for exposta pelos pointcuts. AspectJ define três tipos de

advice: before, after e around. A Figura2.5fornece exemplo de uso de cada um deles.

Neste trabalho, são empregados apenas advice do tipo around.

Advice around: Este tipo de advice é executado no lugar do ponto de junção selecio-

nado. Pode capturar ou executar o ponto de junção inúmeras vezes com possibilidade de

passar diferentes argumentos em cada uma dessas execuções. Dentro do advice usamos

a instrução proceed() para executar o ponto de junção originalmente selecionado. As

linhas 23 a 30 exemplificam o uso do advice around para tratar exceções. A chamada

2.7. PROGRAMAÇÃO ORIENTADA A ASPECTOS

1 - pointcut myPointcut(): execution(void Class.method());

2 -

3 - before(): myPointcut() {

4 -

//Antes da execução de method()

5 -

//faz alguma coisa.

6 - }

7 -

8 - after(): myPointcut() {

9 -

//Depois da execução de method()

10-

//faz alguma coisa.

11- }

12

13- after()returning: myPointcut() {

14-

//ao retornar sem erros da execução

15-

//realiza alguma ação

16- }

17-

18- after() throwing : myPointcut(){

19-

//Realiza alguma ação após method()

20-

//lançar uma uma exceção de qualquer tipo

21- }

22-

23- void around(): myPointcut(){

24-

//Substitui a execução de method()

25-

try {

26-

f = proceed(); //Executa o corpo original de method()

27-

} catch (IOException e) {

28-

...

29-

}

30- }

Figura 2.5 Exemplo de advice em AspectJ

que representa a condição do ponto de junção é capturada pelo pointcut myPointcut

e executada através do comando proceed(). Se levantar alguma exceção, o fluxo de

controle segue para o bloco catch, após isto, retorna para o ponto posterior à captura

do ponto de junção.

Intertype declarations Além de modificar o comportamento de um sistema em

tempo de execução pelo uso de pointcuts e advice, um aspecto pode alterar a estrutura

estática dos módulos do sistema através das declarações intertipo (Intertype Declarati-

2.7. PROGRAMAÇÃO ORIENTADA A ASPECTOS

tes, construtores, exception softening entre outros) que afetam a estrutura e hierarquia de

outros tipos [52]. Como este trabalho tem como foco o tratamento de exceções, vamos

nos ater ao comando declare soft.

O comando declare soft faz com que o compilador Java não realize as verifica-

ções que normalmente faria, com relação a exceções, no conjunto de pontos de junção

selecionado. Ele envolve a exceção selecionada com uma exceção não verificada do tipo

SoftException. A sintaxe é apresentada na linha abaixo.

declare soft : Type : Pointcut ;

Os trechos de código abaixo apresentam exemplos do emprego dessa construção:

declare soft : IOException : execution(Classe.metodoX(..));

declare soft : XException : call(Classe.new(..));

No primeiro exemplo, a exceção IOException não precisa ser tratada dentro da

execução do método metodoX() da classe Classe pois, através deste comando, o

compilador Java não irá verificá-la. Algum outro ponto do programa deverá tratar uma

exceção do tipo SoftException. No segundo exemplo temos uma construção similar.

A exceção XException não precisa ser tratada na chamada ao construtor da classe

Classe pelo mesmo motivo.

3

Abordagem Proposta

Este capítulo tem por objetivo descrever a abordagem proposta. Esta permite o teste do

comportamento excepcional possibilitando especificar e verificar os fluxos excepcionais

percorridos a partir dos pontos onde são lançados até o seu destino. Também apresenta-

mos um exemplo ilustrativo para apontar algumas limitações no uso de casos de testes

elaborados através do JUnit referentes à detecção de erros de tratamento de exceções.

3.1 Motivação

Considere um sistema de informação dividido em três camadas (figura

3.1). A camada

de Dados, responsável pela lógica e comunicação com o banco de dados; Negócio, res-

ponsável pela logica da aplicação e GUI responsável pela lógica de apresentação. Neste

sistema, as exceções levantadas pelo DAO (Data Access Objects), na camada de dados,

são subtipos de DAOException e representam problemas relacionados à tentativa de

acesso ao banco de dados. Estas exceções devem ser repassadas e tratadas pelo servlet

na camada acima (GUI).

Em alguns casos o JUnit silencia o acontecimento de uma exceção. O seguinte caso

de teste ilustra este problema:

public void testDAOEHPolicy (){

Servlet s1 = new Servlet();

s1.service();

}

Se uma exceção do tipo DAOException for lançada no DAO mas não for repas-

sadas até o servlet, pode-se ter a sensação que a exceção foi adequadamente tratada

pelo Servlet. No entanto a exceção pode ter sido acidentalmente capturada por um

3.1. MOTIVAÇÃO

Figura 3.1 Arquitetura em camadas de um sistema de informação

elemento intermediário na cadeia de propagação desta exceção como por exemplo um

método da fachada.

Para dar suporte a criação de casos de testes para a verificação da ocorrência de uma

exceção o JUnit define a anotação@Test(expected=Exception.class1.

O caso de teste abaixo define que na cadeia de chamadas do método service()

deverá ocorrer o lançamento de uma exceção do tipo DAOException e está deverá ser

propagada até o caso de teste.

@Test(expected=DAOException.class)

public void testDAOEHPolicy (){

Servlet s1 = new Servlet();

s1.service();

}

Este teste faz chamada ao método service() do Servlet. O teste espera a ocorrên-

cia de uma exceção do tipo DAOException e caso não ocorra o teste falha.

3.1. MOTIVAÇÃO

Esta abordagem também é limitada porque pode esconder erros sutis. Por exemplo,

se um componente sinaliza uma exceção que representa um erro de programação mas

essa exceção é capturada por um tratador genérico. Podemos exemplificar esta situação

com exemplo abaixo:

public void update(Entity entity) throws DAOException {

try {

...

//neste momento entity é nulo

//gerando NullPointerException

EntityData data = entity.getData()

...

} catch (Exception e) {

throw new DAOException();

}

}

Neste trecho de código, se o objeto passado como parâmetro (entity) for nulo, na

chamada ao método getData() é obtida uma exceção do tipo NullPointerException.

Esta, por sua vez, é capturada pelo tratador genérico e uma exceção do tipo DAOException

é propagada à cadeia de chamadas do método update. O teste elaborado para este tre-

cho de código utilizando o JUnit irá passar e o erro de programação será mascarado.

Observamos neste estudo através dos sistemas avaliados (capítulo

4) erros desta natu-

reza.

O JUnit não fornece uma maneira de verificar se um elemento intermediário errone-

amente lida com a exceção. Isso é compreensível, uma vez o JUnit visa apoiar testes

de unidade. Porém, a propagação global de exceção de forma inesperada pode ser uma

grande fonte de erros [57,55]. Este problema é particularmente visível para linguagens

como Java e C#, onde exceções são objetos que podem ser capturados por seus superti-

pos [55], isto é, um tratador de exceção para um tipo de T captura exceções de qualquer

tipo T!que é um subtipo de T .

JUnit é o padrão para testes automatizados de software e foi estendido em alguns

cenários para lidar com escopo mais amplo (por exemplo, testes de integração) [27,56].

Para este trabalho utilizamos o JUnit em um contexto mais amplo. Isso não impede que

testes de unidades sejam criados paralelamente aos testes do comportamento excepcio-

nal proposto neste trabalho.

Acreditamos que o tratamento de exceções deve ser levado em conta em todas as

fases de desenvolvimento de software [26] e que TDD e métodos ágeis proporcionam

3.1. MOTIVAÇÃO

um melhor suporte que um processo baseado em documentação. Consideramos que a

abordagem de testes no JUnit deve ser empregada em outros contextos, não somente

para testes de unidade do comportamento normal da aplicação. Tendo em vista tais limi-

tações, verificamos a necessidade de uma abordagem para facilitar a definição, criação,

execução e verificação de casos de testes referente ao comportamento excepcional. Os

requisitos fundamentais para essa abordagem são:

• Permitir ao desenvolvedor definir os fluxos excepcionais percorridos por uma exce-

ção e compará-los com o fluxo excepcional obtido pela execução do sistema. Este

fluxo pode ser definido com todos os nós na cadeia de propagação das exceções,

ou parcialmente, onde elementos podem ser omitidos. Fluxos excepcionais que

divergem da especificação são considerados erros de implementação referentes ao

comportamento excepcional da aplicação.

• Os testes devem ser especificados de modo similar aos testes convencionais do

JUnit adicionando as definições dos fluxos excepcionais.

• Devido ao fato dos testes estarem sempre atualizados (caso contrário os testes não

passam), estes podem ser utilizados como a própria documentação no que se refere

aos fluxos excepcionais excluindo a necessidade de manter mais um artefato.

Documentos relacionados