1 /20 MÉTRICAS E QUALIDADE DE SOFTWARE Firmino dos Santos Filho, Brasil [email protected] Abstract Pelo fato da qualidade não possuir uma única dimensão, atributo, ou característica, a noção de qualidade normalmente é capturada em um modelo que descreve as características compostas e as relações entre elas. Para definir um modelo de qualidade, deve haver uma clara distinção entre atributos internos e externos. Qualidade pode ser medida de diversas formas — métricas e critérios devem ser estabelecidos para satisfazer as necessidades do projeto de software. Este artigo discute como aplicar métricas de software para melhorar a qualidade e a confiabilidade de produtos de software, baseado nas melhores práticas de desenvolvimento de software, práticas comercialmente aprovadas. Índice Abstract.....................................................................................................................................1 1. Definições...........................................................................................................................2 2. Melhores Práticas de Software ........................................................................................3 3. Erros, Falhas e Avarias ....................................................................................................6 3.1. Análise da Terminologia sob a ótica da língua Portuguesa.......................................6 3.2. Erros, Falhas(Faults) e Avarias(Failures)...................................................................7 4. Confiabilidade como um Atributo da Qualidade...........................................................8 5. Melhoria do Processo de software com CMM ...............................................................9 6. Métricas de Software para Qualidade. .........................................................................10 6.1. Métricas de Qualidade para Requisitos de Software...............................................11 6.1.1. Use Case (Casos de Uso) .........................................................................................11 6.1.2. Métricas para os Requisitos....................................................................................12 6.2. Qualidade do Código e do Projeto, Métricas de Confiabilidade.............................12 6.2.1. 6.2.1.1. Métricas de Código..................................................................................................13 Tamanho...............................................................................................................13 6.2.1.1.1. Número de classes ................................................................................................13 6.2.1.1.2. Número de linhas executáveis.............................................................................14 6.2.1.2. Volatilidade ..........................................................................................................14 6.2.1.2.1. Breakage ...............................................................................................................14 6.2.1.3. Complexidade.......................................................................................................14 6.2.1.3.1. Tamanho dos métodos .........................................................................................14 6.2.1.3.2. Número de declarações executáveis por método...............................................14 6.2.1.3.3. Número de métodos por classe ...........................................................................14 2 /20 6.2.1.3.4. Número de if-then-else, while, for, try and switch aninhados..........................14 6.2.1.3.5. Número de Caminhos Estáticos..........................................................................15 6.2.1.3.6. Resposta de uma Classe (RFC)...........................................................................15 6.2.1.3.7. Complexidade Ciclomática de métodos .............................................................15 6.2.1.3.8. Profundidade da estrutura de herança ..............................................................16 6.2.1.4. Acoplamento.........................................................................................................16 6.2.1.4.1. Número de filhos ..................................................................................................16 6.2.1.4.2. Acoplamento entre objetos (fan-out da classe)..................................................16 6.2.1.5. Coesão ...................................................................................................................17 6.2.1.5.1. Número de filhos ..................................................................................................17 6.2.1.5.2. Falta de coesão em Métodos (LCOM)................................................................17 6.2.1.6. Legibilidade ..........................................................................................................17 6.2.1.6.1. Número de linhas de comentário por número de declarações ou instruções do programa 17 6.2.1.6.2. Razão Comentários / Código ..............................................................................17 6.3. Qualidade do teste e Métricas de Confiabilidade....................................................17 6.3.1. Casos de Teste..........................................................................................................18 6.3.2. Propósito dos Casos de Teste..................................................................................18 6.3.3. Métricas de Teste.....................................................................................................19 7. Referencias ......................................................................................................................19 1. Definições Na norma ISO 9126, qualidade de software é definido como: "A totalidade de recursos e características de um produto de software que afeta sua habilidade para satisfazer as necessidades definidas ou implícitas". Decorre que a qualidade é decomposta em seis fatores: • Funcionalidade • Confiabilidade • Eficiência • Facilidade de Uso • Facilidade de Manutenção • Portabilidade IEEE 610.12-1990 define confiabilidade como “A habilidade de um sistema ou componente executar as funções exigidas nas condições declaradas por um período de tempo especificado.” IEEE 982.1-1988 define Gerenciamento de Confiabilidade de Software como “O processo de otimização da confiabilidade de software por um programa que enfatiza prevenção de erro de software, detecção e remoção de falhas, e o uso de medidas para maximizar a confiabilidade levando em conta fatores restritivos do projeto tais como recursos, cronograma e desempenho.” 3 /20 Isto posto, a confiabilidade do produto de software entregue é relacionado a qualidade de todos os processos e produtos do desenvolvimento de software; a documentação dos requisitos, o processo de desenvolvimento, o código, planos de teste, e o teste do software. A presença de um bem definido e bem administrado processo é um discriminador fundamental entre hiper-produtivos projetos e projetos malsucedidos. Para melhorar a qualidade do software, o processo de desenvolvimento de software deve ser compreendido, definido, medido, e continuamente melhorado. A definição de processo de desenvolvimento de software definida pela SEI: "Um processo de software é uma série de atividades, métodos, práticas e transformações que as pessoas usam para desenvolver e manter produtos de software.” Em sistemas de software de grande porte, a obtenção de qualidades tais como desempenho, segurança, suscetibilidade à modificações, funcionalidade, confiabilidade, eficiência, facilidade de uso, facilidade de manutenção e portabilidade não só é dependente de práticas de a nível de código mas também de todo o processo de desenvolvimento de software. 2. Melhores Práticas de Software Um processo de desenvolvimento de software é uma série de atividades necessárias para transformar os requisitos do usuário em um sistema de software. Um processo efetivo provê diretrizes para o desenvolvimento eficiente de software com qualidade. Um processo bem definido permitirá resolver o paradoxo de software, maior qualidade com rápida disponibilização para o mercado. O processo adequado captura e apresenta as melhores práticas que o estado atual da arte permite: • Desenvolvimento Iterativo ! Desenvolvimento Iterativo é a técnica que é usada para entregar a funcionalidade de um sistema em uma série sucessiva de liberações de modo a realizar crescentemente o sistema desejado. Os riscos mais críticos devem ser focados nas fases iniciais do projeto de modo a aumentar previsibilidade a evitar descarte e retrabalho [1]. • Gerenciamento de Requisitos (Requirements Management, RM) • Arquiteturas baseadas em componentes ! RM é definido como uma abordagem sistemática para extrair, organizar e documentar os requisitos do sistema e um processo que estabelece e mantém um acordo entre o cliente e os projetistas, de modo a controlar as mudanças de requisitos do sistema. ! O gerenciamento de requisitos envolve a tradução dos pedidos dos clientes em uma série de necessidades fundamentais dos clientes e características de sistema. Estes por sua vez são detalhados em especificações funcionais e não funcionais. Estes requisitos mais específicos são os requisitos de software [2]. ! Uma valiosa ferramenta pode ser usada para detalhar requisitos funcionais: Use Case (Casos de Uso). Um Caso de Uso descreve uma sucessão de ações, executada pelo sistema, que resulta um valor para o usuário. Casos de Uso servem como uma representação UML para os requisitos do sistema. ! Quando é dito que um processo é dirigido a Casos de Uso (Use-case driven) significa que o processo de desenvolvimento segue um fluxo – segue uma série de fluxos de trabalho que derivam dos casos de uso. Casos de Uso são especificados, casos de uso são projetados, e no final casos de uso são a fonte a partir do qual o projetista de teste constrói os casos de teste (test cases). ! Um componente pode ser definido como uma parte substituível de um sistema que cumpre uma função clara no contexto de uma arquitetura bem definida. Um 4 /20 componente adapta-se e provê a realização física de uma série de interfaces e comportamentos. ! Arquitetura baseada em componentes é a base para a reutilização, reutilização de componentes e reutilização de arquiteturas; permite o uso de componentes comercialmente disponíveis e a expansão do software existente incrementalmente [3]. • Foco na Arquitetura • Modelagem Visual (UML) e Métodos Orientados a Objetos • Trabalho de equipe • Ferramentas de apoio • Verificação continua da qualidade ! Extensas análises dos requisitos, do projeto, implementação e atividades de avaliação são executadas antes da implementação completa esteja em foco. O foco inicial do projeto de implementar e testar a arquitetura deve preceder o desenvolvimento em larga escala e teste de todos os componentes. ! Em sistemas de software de grande porte, a obtenção dos requisitos de qualidade não só depende de práticas de a nível de código, mas também de toda a arquitetura do software. Assim, está entre os principais interesses do desenvolvedor, determinar, na ocasião em que a arquitetura de sistema de software for especificada, se o sistema terá as qualidades desejadas. ! UML (Unified Modeling Language) é a terceira geração de linguagem de modelos visuais que define as construções e relações de sistemas complexos. ! A UML é a linguagem sucessora da análise orientada a objetos e métodos de projeto. ! A UML é uma linguagem padronizada para modelagem de software – uma linguagem que permite a visualização, especificação, construção e documentação dos artefatos de sistemas intensivos de software. ! A linguagem UML permite a comunicação clara e precisa entre os diversos membros do time de desenvolvimento. A linguagem UML permite ao desenvolvedor a visualização do produto de seu trabalho em diagramas padronizados. Provendo anotações formalizadas para capturar e visualizar as abstrações de software, o principal impacto da tecnologia orientada a objetos está na redução do tamanho total do que precisa ser desenvolvido. ! Um projeto de software bem sucedido sucesso precisa ter equilíbrio entre um sólido talento e pessoas altamente qualificadas nas posições chaves. Trabalho de equipe é muito mais importante que a soma dos indivíduos. ! Todas as pessoas em organizações de desenvolvimento de software têm que ter uma meta comum: entregar um produto de alta qualidade no prazo e dentro do orçamento previsto. ! As ferramentas e ambiente usados no processo de desenvolvimento de software têm um efeito linear na produtividade do processo. Durante cada ciclo de desenvolvimento de software as ferramentas provêem apoio de automatização crucial, de modo a permitir o desenvolvimento dos artefatos de engenharia de software através de mudanças graduais e controladas. Ferramentas e ambientes de desenvolvimento devem ser vistos como componentes primário para a automatização do processo e melhoria continua. ! Existe uma propriedade famosa no desenvolvimento de software: é muito mais barato corrigir defeitos durante o desenvolvimento que os corrigir depois do desenvolvimento. ! Nos ciclos de vida iniciais as métricas devem ter uma forte contribuição para a avaliação da qualidade, quando esforços para melhorar a qualidade de software são muito mais efetivos. A coleta das métricas deve ser automatizada e não intrusiva, ou seja não deve interferir com as atividades dos desenvolvedores. ! A avaliação do software deve ser uma coleta de tópicos não usuais (geralmente chamado de métricas de software), que possui uma ampla gama de modelos para predizer a qualidade do projeto de software. 5 /20 ! Deve-se utilizar as métricas e indicadores de software para medir o progresso e a qualidade de cada artefato produzido durante o processo de desenvolvimento de software. ! Você não pode desenvolver um produto de software com qualidade ou melhorar o processo sem ter como medir (medir no sentido de avaliar sua qualidade e estágio de desenvolvimento) este software. As medições obtidas do software devem ser analisadas de modo a identificar pontos fracos do processo definido e prover uma forma de se obter uma melhoria do processo utilizado. ! Métricas de software são usadas afim de monitorar e avaliar os seguintes aspectos do projeto: " Progresso em termos de tamanho e complexidade. " Estabilidade em termos de taxa de mudança na implementação, tamanho ou complexidade. " Modularidade em termos da extensão da mudança. " Qualidade em termos do número e tipo de erros. " Maturidade em termos da freqüência de erros. " Recursos em termos de recursos despendidos contra os planejados. ! A visão matemática das métricas pode ser achada nas referências [5] e [7]. • Gerenciamento da configuração de software (SCM, Software configuration management) ! SCM é uma disciplina da engenharia de software que inclui as ferramentas e técnicas (processos ou metodologia) utilizadas pelas empresas para gerenciar as mudanças de seus ativos de software. ! IEEE 828-1998 diz sobre SCM: " SCM constitui uma boa prática de engenharia para todos os projetos de software, independentemente da fase do desenvolvimento, ou se for protótipo rápido, ou manutenção em andamento. Aumenta a confiança e qualidade do software pelas seguintes razões: # Prove estrutura para identificação e controle da documentação, código, interfaces, e bancos de dados para apoiar todas as fases do ciclo de vida. # Apoiando uma metodologia escolhida para desenvolvimento / manutenção que se ajuste aos requisitos, normas, políticas, organização e filosofia de gerenciamento. # Produz gerenciamento e informação de produto relativo ao estado da versão do produto de software utilizado como referencia, controle de mudanças, testes, liberações, auditorias, etc. • Padrões para Codificação (Coding Standards) ! A eliminação de todos os erros está, pelo menos em ambientes industriais atuais, bem além da capacidade tecnológica de software disponível. Não obstante a experiência sugere que a densidade de erros de códigos liberados possam ser reduzidos pela metade através da utilização de processos internos de verificação apoiados por ferramentas analíticas adequadas. Isto não requer mudanças de paradigmas ou idiomas. É necessário a determinação na adoção de técnicas comprovadas. ! De todas as formas de controle de qualidade de software, a inspeção de código é sem dúvida a mais efetiva. Quando apoiado por boas ferramentas, o custo unitário típico de identificação de um erro estático é até duas vezes menor que o custo da identificação através de métodos dinâmicos. Para realizar o trabalho de inspeção você precisa de um padrão de codificação (coding standard) definindo requisitos que o código a ser inspecionado deve satisfazer. [12] ! Um padrão de codificação são regras que governam o uso de uma linguagem de programação. Complementa o padrão da linguagem definindo características de uso aceitável e o inaceitável. Características de uso inaceitável conduzem ao erro ou a má interpretação. Características de uso aceitável evitam situações dúbias ou problemáticas. Isto não garante que seu código esteja livre de erros, porque sempre se pode alcançar 6 /20 uma implementação imaculada da coisa errada. Um padrão de codificação ajuda a que você: " evite uso de características indefinidas, " evite uso de características não especificadas, " evite uso de características definidas pela implementação, " se proteja contra erros de compilação, " se proteja contra erros comuns de programação, " limite a complexidade de seu programa (Veja item 6), " estabeleça uma base objetiva para revisão de seu código. ! Evitando usos indefinidos, não especificados e definidos pela implementação, efetivamente você limita o uso da linguagem de programação a um subconjunto que é inequivocamente definido. Isto evita os pontos fracos linguagem. Este processo não é a prova de falhas mas o ajudará a prevenir erros. Se você examinar relatórios de erros gerados pelo compilador, não só o seu próprio, mas também de outros desenvolvedores, para a mesma linguagem, você obterá uma idéia razoável dos tipos de coisas que podem ser mal implementadas. Você pode evitar essas características da linguagem ou pelo menos sujeitar seu o uso a uma clara e razoável justificação. • Teste Automatizado ! ”Teste Automatizado” está automatizando o processo manual de teste ainda hoje em uso. ! O propósito real das ferramentas de teste automatizadas são automatizar os testes de regressão. Isto significa que você tem que ter ou tem que desenvolver casos de teste detalhados que sejam reproduzíveis, e este conjunto de testes sejam executados toda vez que haja uma mudança no programa de modo a assegurar que esta mudança não produza conseqüências não intencionais. As melhores práticas são um conjunto de técnicas aprovadas comercialmente para o desenvolvimento de software que, quando usadas de modo combinado, golpeiem as causas básicas dos problemas de desenvolvimento de software. 3. Erros, Falhas e Avarias 3.1. Análise da Terminologia sob a ótica da língua Portuguesa A escolha de uma terminologia que exprima na nossa língua conceitos originariamente definidos em Inglês é uma tarefa delicada e difícil. Vamos analisar a opção pela trilogia falha => erro => avaria para designar respectivamente "fault", “error" e "failure". Sendo a tradução de "error" por erro perfeitamente pacifica, toda a polemica se resume a atribuição de termos Portugueses para "fault" e "failure". A palavra “falha” é talvez a mais adequada para ambos os casos, dado não haver na língua portuguesa termos que traduzam com clareza a diferenciação semântica que existe entre os termos "fault" e "failure" assumidos no sentido técnico. De fato, existem em Português diversos termos com significado semelhante tais como "falta", "avaria", "defeito", sendo possível encontrar argumentos para eleger qualquer destes termos como a melhor tradução quer de "fault" quer de "failure". O termo "falha" é normalmente utilizado no sentido de “causa de um evento”. Logo "falha" é o termo português mais adequado para "fault". Um outro argumento a favor da tradução de "fault" por "falha", prende-se com o fato de na área científica o conceito vinculado pelo termo "fault" ser usado com mais freqüência e num conjunto de expressões mais diversificado do que o termo "failure". Assim, havendo uma interdependência na tradução das duas palavras anglo-saxonicas, 7 /20 deve-se reservar a melhor tradução para o termo utilizado com mais freqüência ou seja, para "fault" . Por outro lado, parece-nos que "avaria" traduz em bom português o termo anglosaxonico "failure", ainda que este termo esteja associado a perturbações físicas de um sistema e, normalmente, de caracter permanente. 3.2. Erros, Falhas(Faults) e Avarias(Failures) Nenhum desenvolvedor de software produz um software perfeito na primeira implementação. Assim, é importante o desenvolvedor medir aspectos de qualidade do software. A terminologia utilizada na investigação e análise deve ser precisa, enquanto nos permitindo entender as causas como também os efeitos da avaliação da qualidade e esforços de aperfeiçoamento. De acordo com a norma IEEE std 729 define-se a seguinte terminologia: • Uma falha (fault) acontece quando um erro humano resultar em um resultado indesejado em algum produto de software. Quer dizer, a falha é a codificação do erro humano. Por exemplo, um desenvolvedor poderia entender mal um requisito da interface de usuário, e como conseqüência criar um projeto que inclua o engano. A falha de projeto também pode resultar num código incorreto, como também instruções incorretas no manual de usuário. Assim, um único erro pode resultar em uma ou mais falhas, e uma falha pode residir em quaisquer dos produtos do desenvolvimento. • Por outro lado, uma avaria (failure) é o desvio de um sistema de seu comportamento exigido. Avarias podem ser descobertas antes ou depois da entrega do sistema, como também elas podem acontecer durante os testes ou durante a operação do sistema. É importante notar que estamos comparando o comportamento real do sistema com o comportamento exigido, em lugar de comparar com o comportamento especificado, porque falhas na especificação dos requisitos podem resultar em avarias também. De certo modo, pode-se pensar em falhas e avarias como visões interna e externa do sistema. Falhas representam problemas que o desenvolvedor vê, enquanto avarias são problemas que o usuário vê. Nem toda falha corresponde a uma avaria, porque as condições em que uma falha resulta numa avaria de sistema pode nunca ocorrer. Este caso é facilmente visualizado se você considerar um código contendo uma falha que nunca é executado, ou este código não é executado o suficiente para incrementar um contador que ultrapasse limites inaceitáveis. Durante o teste e a operação, observamos o comportamento do sistema. Quando um comportamento indesejável ou inesperado acontece, nós reportamos este fato como uma avaria. A confiabilidade de um sistema de software está definida em termos de avarias observadas durante a operação, ao invés de número de falhas; normalmente, podemos deduzir pouco sobre confiabilidade a partir somente das informações de falhas. Assim, a distinção entre avarias e falhas é muito importante. Sistemas com muitos falhas podem ser muito confiáveis, porque as condições que ativam as falhas podem ser muito raras. Para muitas organizações, erros freqüentemente significam falhas. Também há uma noção distinta de "processar erro", que pode ser pensado como um estado do sistema que resultou da ativação de uma falha mas antes da ocorrência de uma avaria [4]. Esta noção particular de erro é altamente pertinente a software tolerante a falhas 8 /20 (que está preocupado em como prevenir avarias na presença de erros de processamento) [5], • Anomalias normalmente representam uma classe de defeitos que são improváveis de causar avarias (desde que considerados isoladamente), mas eventualmente podem causar avarias indiretamente. Neste caso, uma anomalia é um desvio do correto, mas não é necessariamente errado. Por exemplo, desvio do padrão aceitável da boa prática de programação (como uso de nomes de variáveis sem significado) é considerado freqüentemente como anomalia. • Defeito normalmente é utilizado para se referenciar coletivamente a falhas e avarias. Porém, às vezes um defeito é uma classe particular de falha. • Bugs refere-se a falhas que acontecem no código. • Crashes são um tipo especial de falha onde o sistema deixa de funcionar. Até que a terminologia seja a mesma para todos, é importante sejam definidas regras claras, de forma que todos as compreendam, quem deve suprir, coletar, analisar, e usar os dados. Freqüentemente, diferenças de significado são aceitáveis, contanto que estes dados possam ser transladados de uma estrutura para a outra. Sistema Codificação do Erro Humano Erro Humano falha avaria 4. Confiabilidade como um Atributo da Qualidade Há muitos modelos diferentes para qualidade de software, mas em quase todos modelos, confiabilidade é um dos critérios, atributo ou característica que estão incorporadas. ISO 9126 std define seis características de qualidade, uma dos quais é confiabilidade. Considerando que confiabilidade é um atributo da qualidade, pode se concluir que a confiabilidade de software depende de um software com muita qualidade. A construção de um software confiável depende da aplicação de atributos de qualidade a cada fase do ciclo de vida de desenvolvimento com a ênfase na prevenção de erros, especialmente nas fases preliminares do ciclo de vida. Métricas são necessárias a cada fase do desenvolvimento para medir atributos da qualidade. IEEE Std 982.2-1988 inclui o diagrama da Figura 1, indicando a relação da confiabilidade com as diferentes fases do ciclo de vida. [8] 9 /20 CONCEITOS • Necessidades/ Objetivos do usuário –funcionalidade –desempenho –completo –consistência • Padrões de Documentação REQUISITOS • Integridade das mudanças • Aderência as necessidades • Cobertura dos Testes de • Arquitetura regressão • Ambiente Operacional • Facilidade de aprendizado, • Completo CONFIABILI facilidade de uso. • Facilidade de uso -DADE DE PROJETO INSTAÇÃO & SOFTWARE • Complexidade VERIFICAÇÃO • Modularidade • Realidade operacional • Interfaces • Cobertura da • Expansibilidade configuração • Cronologia, • Interfaces TESTE IMPLEMENTADimensão – SW / SW • Cobertura das ÇÃO • Completo – SW / HW Funcionalidades • Complexidade • Cobertura dos • Interfaces Tópicos • Completo • Interface dos • Padrões de componentes Desenvolvimento • Medidas de • Facilidade de Desempenho manutenção FIM DE VIDA • Facilidade de transferência, conversão, migração. • Verificação em paralelo. OPERAÇÃO & MANUTENÇÃO Figura 1: Fatores de Qualidade que afetam a Confiabilidade Focando na prevenção de erros para melhoria da confiabilidade, precisamos identificar e medir os atributos da qualidade aplicável as diferentes fases do ciclo de vida. Precisamos especificamente focar o fluxo de trabalho (workflow) – requisitos, análise, projeto, implementação, e teste – que ocorrem nas diversas iterações. 5. Melhoria do Processo de software com CMM Um processo de desenvolvimento de software bem administrado pode ser visto como processo iterativo de troca de informações e permite que organizações de desenvolvimento de software tenham uma clara visão e um bom entendimento da informação disponível, provendo fundamentos para a análise e administração do processo de software baseado em fatos concretos e não em vagas suposições. Para resistir a pressão da competição, economia de recursos, e para melhorar a qualidade do software produzido, uma organização de desenvolvimento de software precisa perceber a necessidade de implementar e administrar um processo de desenvolvimento de software eficiente e efetivo. [13] O Modelo de Maturidade de Capacidade (Capability Maturity Model (CMM)), desenvolvido pela Software Engineering Institute (SEI), é a estrutura básica conceitual que representa o processo de gerenciamento do desenvolvimento de software. O CMM contêm cinco níveis de maturidade ou fases. Estes cinco níveis de maturidade definem uma escala original para medir a maturidade do processo de software de uma organização e para avaliar a capacidade do processo de software. Cada nível de maturidade indica o nível de capacidade de processo: 10 /20 • Nível 1, inicial: O processo de software é caracterizado como ad hoc, caótico, e heróico. Poucos processos estão definidos ou seguidos, e o sucesso do projeto depende do esforço individual. Não há nenhum gerenciamento formal sobre o desenvolvimento de software. • Nível 2, reproduzível: Este nível provê uma introdução formal, ao processo documentado. São estabelecidos processos básicos de gerenciamento para controlar custo, cronograma e funcionalidade. A necessária disciplina do processo está a caminho de repetir sucessos de projetos anteriores com aplicações semelhantes. A elevação do Nível 1 para o Nível 2 significa que a organização estabeleceu controle sobre o gerenciamento de projeto, estabeleceu um grupo de processo de engenharia de software (SEPG), e formalmente introduziu métodos e técnicas de engenharia de software. • Nível 3, definido: Este nível provê os fundamentos para o processo de melhoria contínua estabelecendo as funções do gerenciamento do processo necessárias para controlar os parâmetros do processo. O processo de software para as atividades de gerenciamento e engenharia estão documentados, padronizados, e integrados em um processo de software padronizado para toda a organização. Todos os projetos usam uma versão sob medida (subset adequado ao projeto) do processo de software padrão da organização para desenvolver e manter software. • Nível 4, gerenciado: Medidas detalhadas do processo de software e da qualidade do produto são coletadas. O processo de software e de produtos são quantitativamente compreendidos e controlados. • Nível 5, otimizado: O processo de melhoria contínua é possível através da avaliação quantitativa do processo e a partir da introdução de idéias e tecnologias inovadoras. 6. Métricas de Software para Qualidade. Devem ser usadas métricas de software para melhorar a qualidade e a confiabilidade através da identificação de áreas da especificação de requisitos do software e do código que potencialmente podem causar erros. Medidas oferecem visibilidade de como os processos, produtos, recursos, métodos, e tecnologias de desenvolvimento de software se relacionam entre si. Medidas podem nos ajudar a responder perguntas sobre a efetividade de técnicas ou ferramentas, sobre a produtividade de atividades de desenvolvimento e a qualidade dos produtos. Modelos de qualidade normalmente envolvem formas de avaliação do produto, e sua meta fundamental é prever a qualidade do produto. Os produtos de um Processo de Desenvolvimento de Software são os artefatos, incluindo o software, documentos e modelos. As métricas recomendadas são listadas aqui com os modelos para os quais se aplicam. 11 /20 Três fases do ciclo de vida serão abordadas onde técnicas de prevenção de erro e métricas de software podem ser aplicadas com grande impacto sobre a qualidade e a confiabilidade: requisitos, codificação e teste. 6.1. Métricas de Qualidade para Requisitos de Software Os requisitos especificam a funcionalidade que deve ser incluída no software final. É crítico que os requisitos sejam escritos em linguagem clara e precisa de modo a facilitar o entendimento entre o desenvolvedor e o cliente. Usando os atributos de qualidade para a confiabilidade mostrado na Figura 1, para software de alta confiabilidade, os requisitos devem ser estruturados, completos, e de fácil aplicação. Há três formatos primários para estrutura de especificação de requisitos, definidos por IEEE, DOD e NASA. [7,8,9]. Estes definem o que deve conter a especificação dos requisitos além dos próprios requisitos. O uso consistente de um formato como estes, assegura que informações críticas, como o ambiente operacional, não sejam omitidas. Requisitos completos são estáveis e incluem todas as informações possíveis, especificado com detalhes adequados de modo a permitir que o projeto e a implementação evoluam. Especificações de requisitos não deveriam conter frases tal qual ASD (a ser definido) ou ASA (a ser adicionado) pois a generalidade destas frases provocam um impacto negativo no projeto, e causam uma arquitetura incoerente. Para aumentar de a facilidade de uso dos requisitos, eles são escritos normalmente na língua corrente (português no Brasil) [ao invés de um estilo de escrita especializado (por exemplo, anotação Z)] que pode facilmente produzir terminologia ambígua. Para desenvolver software confiável nas fases subsequentes a fase de requisitos, os requisitos não devem conter termos ambíguos, ou conter qualquer terminologia que possa ser interpretado como um requisito opcional. Requisitos ambíguos são estes que podem ter significados múltiplos ou que parecem deixar ao desenvolvedor a decisão se deve ou não implementar um requisito. A importância de documentar requisitos corretamente foi uma das razões para que a indústria de software produzisse um número significante de ferramentas de auxilio à criação e gerenciamento de documentos da especificação de requisitos e dos itens especificados individualmente. Entretanto poucas destas ferramentas ajudam na avaliação da qualidade do documento de requisitos ou dos itens especificados individualmente. 6.1.1. Use Case (Casos de Uso) Uma boa ferramenta para capturar requisitos e coloca-los no contexto é o modelamento de Casos de Uso (Use Cases). Casos de Uso são um modo para organizar requisitos da perspectiva do usuário. Todas os requisitos para um usuário realizar uma tarefa particular são reunidos em um único caso de uso. O modelo de use-case é a coleção de todos os casos de uso individuais. Vantagens do modelamento de casos de uso incluem: • Casos de uso mostram porque o sistema é necessário. Casos de uso mostram que metas os usuários podem realizar usando o sistema. • Requisitos de sistema são definidos no contexto. Cada requisito é parte de uma sucessão lógica de ações de modo a realizar uma meta do usuário. 12 /20 • • Casos de uso são facilmente compreensíveis. O modelo expressa requisitos da perspectiva do usuário e na linguagem própria do usuário. O modelo nos mostra como os usuários imaginam o sistema e o que o sistema deve fazer. O modelo é um meio para comunicar os requisitos entre os clientes e o desenvolvedor, de modo a garantir que o sistema que construímos é o que deseja o cliente. 6.1.2. Métricas para os Requisitos Característica Tamanho Esforço Volatilidade Qualidade Rastreabilidade Completo Métricas • Número totais de requisitos • Número de Casos de Uso • Unidades de homens-hora (na elaboração, mudança e reparo, separadamente). • Número de defeitos e pedidos de mudança (aberto, fechado) • Defeitos–número de defeitos, por severidade, abre, fechado • Requisito para UC • Modelo de UC para o projeto • Projeto – Cenários realizados no modelo de projeto / número totais de cenários • Implementação – Cenários realizados no modelo de implementação / número totais de cenários. • Teste – Cenários realizados no modelo de teste (casos de cases) / número totais de cenários. • Número de Caso de Uso completados (revisados e sob o gerenciamento da configuração e sem defeitos a serem resolvidos) / casos de uso identificados (ou número estimado de casos de uso) 6.2. Qualidade do Código e do Projeto, Métricas de Confiabilidade Métricas tem que contribuir para a avaliação da qualidade nas fases iniciais do ciclo de vida, quando esforços para melhorar a qualidade de software são efetivos. A coleta das métricas deve ser automatizada e não intrusivo, isto é, não deve interferir com as atividades dos desenvolvedor. A definição de um procedimento de métricas para códigos orienta os projetos de software e tem como finalidade monitorar e avaliar os seguintes aspectos do projeto: • Progresso em termos de tamanho e complexidade. • Estabilidade em termos de taxa de mudança na implementação, tamanho, ou complexidade. • Modularidade em termos da extensão de mudança • Qualidade em termos do número e tipo de erros. • Maturidade em termos da freqüência dos erros. • Recursos em termos do valor gasto com o projeto contra o gasto planejado. Um modelo de qualidade tem que definir os atributos a serem medidos, atributos internos e externos: 13 /20 • Atributos internos - Medimos e analisamos atributos internos porque eles podem prognosticar atributos externos. Os atributos internos estão freqüentemente disponíveis para medida no inicio do ciclo de vida, considerando que atributos externos só são mensuráveis quando o produto estiver completo (ou quase assim), e segundo, atributos internos são freqüentemente mais fáceis de medir que atributos externos: Características a serem medidas Característica Tamanho Esforço Volatilidade Qualidade Completo • Métricas • Número de classes • Número de linhas executáveis • Horas homem. • Número de defeitos e pedidos de mudança (aberto, fechado) • “Breakage” (veja 6.2.1.2.1) para cada correção ou mudança devido a melhoramento. Complexidade • Tamanho de métodos • Número de declarações executáveis por método • Número de métodos por classe • Número de declarações if-then-else, while, for, try e switch aninhados. • Contagem estática de caminhos • Resposta para uma Classe (RFC) • Complexidade Ciclomática dos métodos • Profundidade da estrutura de herança de classes Acoplamento • Número de objetos filhos. • Acoplamento entre objetos (classe fan-out). Coesão • Número de objetos filhos. • Falta de coesão nos métodos (LCOM). Defeitos • Número de defeitos, por severidade, aberto, fechado. Legibilidade • Número de linhas de comentário por número de declarações do programa. • Razão comentário / linha de código. • Número de unidades de classes testadas / número de classes no modelo de projeto • Número de classes integradas / número de classes no modelo de projeto • Rastreabilidade da implementação (no modelo de Casos de Uso). • Rastreabilidade do modelo de Teste. • Tempo da integração ativa e de teste do sistema. Atributos externos - esses que só podem ser medidos considerando-se como o produto se relaciona a seu ambiente: o Facilidade de uso o Facilidade de manutenção o Medidas de qualidade baseadas nos defeitos. 6.2.1. Métricas de Código 6.2.1.1. Tamanho 6.2.1.1.1. Número de classes Número de classes é um dos modos para se medir Tamanho. Esta medida pode ser usada para verificação do plano da iteração e da quantidade de retrabalho por iteração. 14 /20 6.2.1.1.2. Número de linhas executáveis Tamanho é usado para se avaliar a facilidade de compreensão do código por desenvolvedores e mantenedores. Expressões executáveis é a contagem do número de linhas executáveis indiferentemente do embora número de linhas físicas de código, por exemplo, if (temp < 0) { // temp = 0; //Este exemplo tem uma sentença executável. } 6.2.1.2. Volatilidade 6.2.1.2.1. Breakage “Breakage” é a tendência do software em apresentar defeitos em muitos lugares toda vez que é mudado. Freqüentemente a quebra (“Breakage”) acontece em áreas que não têm nenhuma relação conceitual com a área que foi mudada. Tais erros enchem os corações dos gerentes de maus pressentimentos. Toda vez que eles autorizam uma correção, eles temem que o software quebre de algum modo inesperado. A medida que a fragilidade se intensifica, a probabilidade da ocorrência de “Breakage” aumenta com o tempo, se aproximando assintoticamente. Tal software é impossível de se manter. Toda correção piora a situação, introduzindo mais problemas do que os que são resolvidos. 6.2.1.3. Complexidade 6.2.1.3.1. Tamanho dos métodos É muito recomendado evitar funções longas (mais do que 100 linhas), porque funções longas têm muitas desvantagens: • Se uma função for muito longa, pode ser difícil sua compreensão. Geralmente, pode se dizer que uma função não deveria ser mais longa que duas páginas, visto que isto é quanto se pode compreender de uma única vez. • Funções complexas são difíceis de se testar. Se uma função consiste em 15 declarações “if” aninhados, então há 215 (ou 32768) diferentes ramos para se testar em uma única função. 6.2.1.3.2. Número de declarações executáveis por método O número de máximo de declarações por método deve ser menor que 10, porque métodos com muitos declarações são difíceis de se entender. 6.2.1.3.3. Número de métodos por classe É altamente recomendado definir o número de máximo de métodos por classe em 20, porque é importante ter uma implementação clara e muitos métodos aumentam a complexidade e obscurecem a compreensão. 6.2.1.3.4. Número de if-then-else, while, for, try and switch aninhados O número de máximo de declarações if-then-else, while, for, try and switch aninhados por classe devem ser menor que 4, porque se um número maior de declarações aninhadas são usadas, mais difícil se torna manter um claro entendimento. 15 /20 6.2.1.3.5. Número de Caminhos Estáticos O número de caminhos estáticos de um método, isto é, o limite máximo de todos os possíveis caminhos de um método, deve ser menor que 20, porque o número de caminhas estáticos está diretamente relacionado a facilidade de teste do software. 6.2.1.3.6. Resposta de uma Classe (RFC) O número de máximo de métodos que podem ser invocados em resposta a uma mensagem deve ser 20. A RFC é a contagem de todos os métodos que podem ser invocados em resposta a uma mensagem de um objeto de uma classe ou por algum método da classe. Isto inclui todos os métodos acessíveis dentro da hierarquia da classe. Esta métrica foca na combinação da complexidade de uma classe através do número de métodos e a quantidade de comunicação com outras classes. Quanto maior for o número de métodos de uma classe que podem ser invocados através de mensagens, maior a complexidade da classe. Se um grande número de métodos podem ser invocados em resposta a uma mensagem, o teste e depuração da classe se torna complexo porque irá requerer um conhecimento mais detalhado da mesma por parte de quem efetua os testes. A elaboração de uma lista com os piores casos possíveis de respostas ajudará na distribuição apropriada do tempo de teste. 6.2.1.3.7. Complexidade Ciclomática de métodos Complexidade Ciclomática é a métrica mais amplamente utilizada, membro da classe de métricas estáticas de software. Introduzida por Thomas McCabe em 1976, mede o número caminhos linearmente independentes de um módulo de programa. Esta medida provê um único número ordinal que pode ser comparado à complexidade de outros programas. A Complexidade Ciclomática é freqüentemente referenciada simplesmente como complexidade do programa, ou como complexidade de McCabe. Pretende ser independente da linguagem e do formato da linguagem [15]. A complexidade ciclomática de um módulo de software é calculada a partir de um gráfico conectado do módulo: Complexidade Ciclomática (CC) = E - N + p Onde E = o número de extremidades do gráfico N = o número de nós do gráfico p = o número de componentes conectados Estudos mostram uma correlação entre a complexidade Ciclomática de um programa e sua freqüência de erro. Uma baixa complexidade Ciclomática contribui para a facilidade de compreensão de um programa e indica que este é acessível a modificações com risco menor que um programa mais complexo. A complexidade Ciclomática de um módulo também é um forte indicador de sua facilidade de teste. Complexidade Ciclomática 1-10 11-20 21-50 maior que 50 Complexidade Ciclomática Avaliação de Risco Programa simples, sem muito risco. Complexo, risco moderado. Complexo, Programa de alto risco. Programa quase impossível de ser testado. (altíssimo risco) 16 /20 Exemplo: 6.2.1.3.8. Profundidade da estrutura de herança Representa o número de derivações da mais afastada classe de base (origem) até esta classe. Um valor elevado desta métrica pode indicar que a classe depende de funcionalidade acumulada, que potencialmente dificulta a compreensão da classe. Quanto mais em baixo uma classe está dentro da hierarquia, maior o número de métodos que provável herdará, o que torna muito mais complexo predizer seu if x>4 no while Do y=2 yes then do this until z=3 seqüência: 1-2+2=1 if / then: 3-3+2=2 while loop: 3-3+2=2 until loop: 3-3+2=2 comportamento. Estruturas com ramos mais longos (profundos) ocasionam uma maior complexidade de projeto, visto que mais métodos e classes estão envolvidos, mas temos também um maior potencial para reuso de métodos herdados. Maior profundidade de Herança indica um compromisso entre um incremento na complexidade e um incremento em reuso. Evite profundidade da estrutura de herança maior que 5. 6.2.1.4. Acoplamento 6.2.1.4.1. Número de filhos O maior o número de filhos (classes derivadas), maior o reuso, visto que herança é uma forma de reuso. Maior o número de filhos, maior a probabilidade de uma abstração imprópria da classe de mãe. Se uma classe tiver um grande número de filhos, pode ser um caso de abuso de subclasse. Se uma classe tiver um grande número de filhos, pode requerer mais tempo para teste dos métodos daquela classe. Evite número de filhos maior que 5. 6.2.1.4.2. Acoplamento entre objetos (fan-out da classe) É definido como o número de classes com os quais uma classe está acoplada. Duas classes estão acopladas quando métodos declarados em uma classe usa métodos ou instancias de variáveis da outra classe [6]. Acoplamento excessivo entre classes de objetos é prejudicial ao projeto modular e impede o reuso. Quanto mais independente uma classe é, mais fácil é reutiliza-la em outra aplicação. Para melhorar a modularidade e promover o encapsulamento, acoplamento entre classes devem ser mínimos. Quanto maior o número de acoplamentos, maior a sensibilidade à mudanças em outras partes do projeto, e como conseqüência a manutenção se torna mais difícil. Forte acoplamento torna um sistema mais complexo visto que uma classe é de difícil compreensão, 17 /20 mudança e correção por si só, se estiver relacionado com outras classes. O projeto de sistemas com o menor acoplamento possível entre classes pode reduzir a complexidade. Isto melhora a modularidade e promove o encapsulamento. Evite número de outras classes com as quais uma classe está acoplada maior que 10. 6.2.1.5. Coesão 6.2.1.5.1. Número de filhos Veja item 6.2.1.4.1. 6.2.1.5.2. Falta de coesão em Métodos (LCOM) A falta de coesão mede a dessemelhança de métodos em uma classe por instancias de variáveis ou atributos. Um módulo altamente coeso deve estar isolado; alta coesão indica boa subdivisão de classes. Falta de coesão ou baixa coesão aumenta a complexidade e aumenta a probabilidade de erros durante o processo de desenvolvimento. Coesão alta indica simplicidade e alta grau de reuso. Classes com baixa coesão provavelmente poderiam ser subdivididas em duas ou mais subclasses com maior coesão. Quando os objetos tem poucos métodos em comum, há muita falta de coesão. Isto implica que um nível de abstração adicional é necessária – objetos semelhantes precisam ser agrupados criando classes derivadas para estes. 6.2.1.6. Legibilidade 6.2.1.6.1. Número de linhas de comentário por número de declarações ou instruções do programa O código de fonte deve ser suficientemente documentado. Por arquivo, pelo menos, uma linha de comentário é necessária para cada duas declarações de programa. Software que não é bem documentado pode ser de difícil compreensão. Especialmente exceções que acontecem no software e que podem ser facilmente mal compreendidas. 6.2.1.6.2. Razão Comentários / Código A razão comentários por código é definido como o número de caracteres visíveis em comentários dividido pelo número de caracteres visíveis fora dos comentários. Delimitadores de comentários são ignorados. Espaços em branco em “strings” são tratados como caracteres visíveis. Um valor alto desta métrica pode indicar que há muitos comentários–um atributo que pode tornar um módulo difícil de se ler. Um valor pequeno pode indicar que não há o bastante. É recomendado ter uma razão comentário por código entre 0,5 e 1,5. 6.3. Qualidade do teste e Métricas de Confiabilidade Métricas de teste devem ter dois critérios para avaliar a confiabilidade de modo abrangente. O primeiro critério é a avaliação do plano de teste, de modo a assegurar que o sistema contenha a funcionalidade especificada nos requisitos (rastreabilidade do teste). Esta atividade deve reduzir o número de erros devido a falta de funcionalidade esperada. O segundo critério, que freqüentemente está associado com a confiabilidade, é a avaliação do número de erros no código e taxa de erros achados/corrigidos. 18 /20 Para assegurar que o sistema contenha a funcionalidade especificada, planos de teste contendo múltiplos casos de teste são escritos; cada caso de teste é baseado em um estado do sistema e testa algumas funções baseadas num grupo de requisitos. O objetivo de um programa efetivo de verificação é assegurar que todo requisito seja testado, o que implica que se o sistema passa no teste, a funcionalidade requerida está no sistema a ser entregue. 6.3.1. Casos de Teste Considerando-se todos os casos de teste, cada requisito deve ser testado pelo menos uma vez, e alguns requisitos serão testados várias vezes porque eles estão envolvidos em múltiplos estados do sistema, em vários cenários e de modos diferentes. Mas como sempre, o tempo e os recursos financeiros disponíveis são questões importantes; enquanto cada requisito dever ser abrangentemente testado, tempo e orçamento limitados sempre são restrições para se escrever e executar os casos de teste. É importante assegurar que cada requisito seja adequadamente, mas não excessivamente, testado. Em alguns casos, os requisitos podem se agrupados usando como critério que os mesmos sejam críticos para o sucesso do projeto; estes devem ser extensivamente testados. Em outros casos, podem ser identificados requisitos não críticos; se um problema acontece, a funcionalidade destes não afeta o sucesso do projeto enquanto se obtém um teste bem sucedido. 6.3.2. Propósito dos Casos de Teste • • • • • Identificar e descrever as condições de teste a serem utilizados. • Estender os Casos de Uso adicionando respostas do sistema • Criar instancias de Casos de Uso (fluxo básico / fluxo alternativo) Identificar os dados específicos necessário ao teste. • Estender o Caso de Uso com vetores de teste (valores). • Estender as pré e pós condições com condições reais do sistema . Identificar os resultados esperados do teste. • Criar critérios de Aprovação/Falha. Se o objetivo do teste for o sistema completo, o caso de teste corresponderá a uma execução completa de um caso de uso. Se o objetivo do teste for um subsistema, o caso de teste corresponderá à parte do caso de uso que corresponde ao subsistema. 19 /20 6.3.3. Métricas de Teste Característica Métricas $%Número de Casos de Teste, Procedimentos de Teste, Tamanho “Scripts” de Teste. $%Número Esforço de horas-homem (despendido com a produção, modificação e correção, separadamente) para a produção dos casos de teste. Volatilidade $%Número defeitos e requisições de mudanças (aberto, fechado) – comparado com o modelo de teste. $%Defeitos – número de defeitos por grau de severidade, Qualidade abertos, fechados (estes defeitos são identificados a partir do modelo de teste, não defeitos identificados pelo time de teste no software) $%Número de casos de teste implementados / número de Completo casos de teste estimados. $%Rastreabilidade do teste (no modelo de Casos de Uso) $%Rastreabilidade do teste (nos Atributos dos Requisitos) $%Cobertura do código Rastreabilidade $%Número de Casos de Teste reportados como bem sucedidos no Resumo de Avaliação do Teste / Número de casos de teste. 7. Referencias [1] Jacobson, Ivar, Grady Booch and James Rumbaugh, "The Unified Software Development Process," Addison-Wesley, 1999. [2] Leffingwell, Dean, "Managing Software Requirements," Addison-Wesley, 2000. [3] Royce, Walker, "Software Project Management," Addison-Wesley, 1998. [4] Laprie, J.C., "Dependability: Basic Concepts and Terminology," Springer-Verlag, 1992. [5] Fenton, Norman E., "Software Metrics," PWS, 1997. [6] Douglas, Bruce Powel, "Real Time UML," Addison Wesley, 1998. [7] Whitmire, Scott A., "Object Oriented Design Measurement", John Wiley & Sons, 1997. [8] IEEE Standard 982.2-1987, "Guide for the Use of Standard Dictionary of Measures to Produce Reliable Software". [9] Rosenberg, Dr. Linda H., Ted Hammer and Jack Shaw, "Software Metrics and Reliability," 9th International Symposium, November, 1998, Germany. 20 /20 [10] Basili, Victor R., Lionel C. Briand and Walcélio L. Melo, "A Validation of ObjectOriented Design Metrics as Quality Indicators", IEEE Transactions on Software Engineering, Vol. 22, nº 10, October, 1996. [11] Martin, Robert C., "Design Principles and Design Patterns", Object Mentor, 2000. [12] "Why Coding Standards?", PR:QA White paper Series: WP5.1, Programming Research Ltd., http://www.prqa.co.uk, 1998. [13] Raynus, Joseph, "Software Process Improvement with CMM", Artech House, 1998. [14] Rosenberg, Dr. Linda H., "Applying and Interpreting Object oriented Metrics", Software Technology Conference, Utah, April 1998. [15] McCabe, Thomas J. & Watson, Arthur H. "Software Complexity.”, Crosstalk, Journal of Defense Software Engineering 7, 12 (December 1994): 5-9.