teste unitário

Post on 13-Jul-2015

141 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Teste

Estilo de desenvolvimento guiado pela criação de testes que validam a funcionalidade implementada antes da criação do código de produção

◦ Código mais simples

◦ Algoritmos confiáveis

◦ Menor custo de manutenção

Aumento de complexidade em cenários de testes funcionais (interface de usuários, repositórios)

Apoio gerencial

Testes falhos causam programas falhos

Testes de cobertura

Lacunas de testes

Falso senso de segurança (TDD não é bala de prata)

Testes requerem visibilidade dos métodos

Encapsulamento requer esconder métodos

Use nomes de pacotes iguais ◦ Classe de produção está no pacote com.foo.bar

◦ Classe de teste deve ficar no pacote com.foo.bar

Use modificadores protected

e package (default)

Testes requerem comportamento específicos

Testes requerem objetos controláveis

Testes requerem Mocks

Frameworks de Mocks◦ Mockito

◦ EasyMock

◦ PowerMock

Framework de Mock◦ Simples

◦ Claro

◦ Intuitivo (depois que você entende sua lógica)

◦ Versão atual 1.9.0

◦ Documentação e notícias http://code.google.com/p/mockito/

@Test

public void myTest() {

SomeObject myMock = Mockito.mock(SomeObject.class);

}

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations =

{"/applicationContext.xml"})

public class SourceCheckerTest {

@Autowired

SourceChecker sourceChecker;

@Mock

SourceRepository mockRepository;

@Before

public void before() {

MockitoAnnotations.initMocks(this);

}

Adiciona um comportamento a um dado método de um mock

public SourceVO getSource(RequestVO request) {

SourceVO sourceVO = null;

sourceVO = repository.loadById(request.getSourceId());

}

@Test

public void procuraSourceExistenteTest() throws RepositoryException {

SourceVO sourceVO = getSourceVO();

String sourceId = sourceVO.getId().toString();

Mockito.when(mockRepository.loadById(sourceId)).thenReturn(sourceVO);

RequestVO request = Given.request();

request.setSourceId(sourceId);

SourceVO source = sourceChecker.getSource(request);

}

//Retorna dado valor

Mockito.when(mockRepository.loadById(sourceId)).thenReturn(sourceVO);

//Retorna exceção

Mockito.when(mockRepository.loadById(sourceId)).thenThrow(new NullPointerException());

//Chama o método real

Mockito.when(mockRepository.loadById(sourceId)).thenCallRealMethod();

//Cria uma resposta customizada

Mockito.when(mockRepository.loadById(sourceId)).thenAnswer(…);

//Retorna dado valor

Mockito.when(counter.getId()).thenReturn(1).thenReturn(2).thenReturn(3). thenThrow(new

NullPointerException());

É possível programar ações consecutivas para um dado método. Muito útil quando se quer ações diferentes a cada chamada de método. Ex.: Loop

Mock é feito com a comparação de objetos, muitas vezes não temos o objeto exato para se compararMockito.when(lotteryMock.theChosenOne(Mockito.anyMap())).thenReturn(categId);

Mockito.any();

Mockito.any(CampaignVO.class);

Mockito.anyBoolean();

Mockito.anyInt();

Mockito.anyCollection();

Mockito.anyCollectionOf(String.class);

Mockito.anyList();

Mockito.anyListOf(Integer.class);

Mockito.anyVararg();

Matchers customizados também podem ser criados mas a sua utilização é muito rara

Métodos void são um caso a parte para o Mockito pois o when recebe o método a ser mockado como parâmetro e o compilador java não aceita isso

public static void main(String[] args) {

System.out.println(getNum()); //OK

System.out.println(getVoid()); // ERROR

}

private static Integer getNum() {

return 5;

}

private static void getVoid() { }

Uso dos métodos doThrow e doCallRealMethod

Mockito.doThrow(NullPointerException.class).when(sourceRepository).loadById("123456");

Mockito.doCallRealMethod().when(sourceRepository).loadById("123456");

Os mocks conseguem retornar objetos controlados mas as vezes deseja-se interceptar a chamada para alterar a resposta, verificar os parâmetros passados e executar ou não o método real para isso existe o Answer

Evite usar answer, procure soluções mais simples.

Dentro do AdServer o Answer foi usado para validar a passagem de argumentos em métodos complexos

public void makeSearch(CampaignVO campaign) {

changeCampaignName(campaign);

changeOtherValue(campaign);

repository.search(campaign); // What are the campaign's attributes?

}

Ex.: Desejo validar a campanha passada no repositório já que essa campanha pode ter sido alterada por métodos intermediários

Mockito.doAnswer(new Answer<Map<Long, Double>>() {

public Map<Long, Double> answer(InvocationOnMock invocation) {

Object[] args = invocation.getArguments();

// valida país recebido

List<AdVO> adVO = (List<AdVO>) args[0];

Assert.assertTrue(adVO.size() == 1);

Assert.assertEquals("728_90", adVO.get(0).getDimension());

return values;

}

}).when(adSelectorSpy).createAdsValues(adList);

Valido se o primeiro argumento passado possui um elemento e se esse elemento tem um valor específico

O que acontece se você deseja controlar apenas uma parte de um objeto? ◦ Ex.: Deseja-se testar um método que chama outro

método dentro da mesma classe

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations =

{"/applicationContext.xml"})

public class SourceCheckerTest {

@Autowired

SourceChecker sourceChecker;

SourceChecker checkerSpy;

@Before

public void before() {

checkerSpy = Mockito.spy(sourceChecker);

}

Espiamos o comportamentode um método real

Um pouco de teoria

◦ Quando um método é mockado ele é executado, porém como ele não existe, não há problema

◦ Mockar um método de um objeto espionado faz o método ser executado, porém como ele é real isso pode causar problemas

◦ Spies devem usar métodos doThrow, doReturn e doNothing

◦ MOCKS NUNCA executam métodos reais até que digamos o contrário (thenCallRealMethod ou doCallRealMethod), SPIES SEMPRE executam métodos reais até que digamos o contrário (doNothing)

@Autowired

TestFilter filter;

TestFilter filterMock;

@Test

public void filtroNaoPreValidadoTest() {

filterMock = Mockito.spy(filter);

Mockito.doReturn(false).when(filterMock.preValidate(Mockito.any(CampaignVO.class)));

List<CampaignVO> campaignList = createCampaignList();

filterMock.doFilter(createSource(), campaignList);

}

O Mockito também consegue validar o comportamento dos métodos reais, verificando quantas vezes um método foi ou não chamado, em qual ordem, etc

A validação de comportamento pode ser muito útil em classes de transformações ou validadores

O Mockito também consegue validar o comportamento dos métodos reais, verificando quantas vezes um método foi ou não chamado, em qual ordem, etc

A validação de comportamento pode ser muito útil em classes de transformações ou validadores

Valido que uma chamada não ocorreu

@Autowired

TestFilter filter;

TestFilter filterMock;

@Test

public void filtroNaoPreValidadoTest() {

filterMock = Mockito.spy(filter);

Mockito.doReturn(false).when(filterMock.preValidate(Mockito.any(CampaignVO.class)));

List<CampaignVO> campaignList = createCampaignList();

filterMock.doFilter(createSource(), campaignList);

// Não passou na prevalidação não deve chamar o filtro

Mockito.verify(filterMock, Mockito.never()).filter(Mockito.any(CampaignVO.class),

Mockito.any(SourceVO.class));

}

Testar um validador.

public boolean validatesCampaignToAlter(CampaignVO campaignVO) {

boolean resp = false;

if (validateBanners(campaignVO) && validateStartDate(campaignVO) &&

validateCategory(campaignVO) && validateCountry(campaignVO) && validateId(campaignVO)) {

resp = true;

}

return resp;

}

@Test

public void alteracaoCampanhaNegadaSemBanner() {

CampaignVO campaign = new CampaignVO();

campaign.setHas_banners((byte) 0);

Assert.assertFalse(validatorSpy.validatesCampaignToAlter(campaign));

Mockito.verify(validatorSpy, Mockito.times(1)).validateBanners(campaign);

Mockito.verify(validatorSpy, Mockito.never()).validateStartDate(campaign);

Mockito.verify(validatorSpy, Mockito.never()).validateCategory(campaign);

Mockito.verify(validatorSpy, Mockito.never()).validateCountry(campaign);

Mockito.verify(validatorSpy, Mockito.never()).validateId(campaign);

Testes muitas vezes obriga a mudar atributos privados de uma classe, como suas dependências.

O Spring Testing possui uma classe chamada ReflectionTestUtils com vários métodos úteis

// Adiciona o objeto sourceCheckerMock dentro do objeto businessMock,

// sendo esse atributo chamado “sourceChecker”

ReflectionTestUtils.setField(target, “atrributeName", objectToInsert);

Boolean slaveCrash = (Boolean) ReflectionTestUtils.getField(target, “attributeToRead");

O Spring Testing também possui algumas ferramentas para se testar controllers e emular chamadas HTTP

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration({"/applicationContext.xml", "/springmvc-servlet.xml"})

public class CacheStatusControllerTest {

@Autowired

private ApplicationContext applicationContext;

@Autowired

private CacheStatusController controller;

private MockHttpServletRequest request = new MockHttpServletRequest();

private MockHttpServletResponse response = new MockHttpServletResponse();

private AnnotationMethodHandlerAdapter handler;

Deve-se carregar o contexto da aplicação e o do MVC

Deve-se carregar o controller necessário

Deve-se criar um handler e um Mock do Request e Response

@Before

public void before() {

// Recupera handler para a chamada ao Controller

handler = applicationContext.getBean(AnnotationMethodHandlerAdapter.class);

// Habilita recuperação do conteúdo do response

response.setOutputStreamAccessAllowed(true);

}

O Handler deve ser associado o Handler usado no Spring (talvez seja um pouco chato descobrir isso)

Caso deseje-se ler o conteúdo do response, o que é o mais comum, deve-se setar o acesso ao OutputStream

@Test

public void validateCacheStatus() throws Exception {

// Prepara método e url de chamada

request.setMethod("GET");

request.setRequestURI("/status/cache.html");

request.setParameter(“update", “true");

handler.handle(request, response, controller);

Assert.assertEquals("Informações incorretas", expectedResponse(),

response.getContentAsString());

}

O request deve ser setado com o método desejado (GET,POST,PUT,etc)

A URL de ser setada na URI do request

Caso haja parâmetros esses devem ser setados no request

Para executar a chamada utilize o método handle do Handler capturado

Para ler a resposta, caso tenha setado essa opção no response, utilize o método getContentAsString (ou outro formato desejado)

Quando utilizar o Spring como seu framework de Injeção de Dependência, prefira sempre as configurações por anotações ao invés do XML, porém vale lembrar que essa última configuração sobrescreve qualquer anotação feita.

Certos cenários de teste necessitam de situações especiais, não hesite em configurar o contexto para que a situação seja contemplada. Ex.: Ligar ou desligar transações, ler outro arquivo de propriedades, ligar ou desligar aspectos, etc.

Mockito◦ http://code.google.com/p/mockito/

TDD◦ http://pt.wikipedia.org/wiki/Test_Driven_Developm

ent

Spring Framework◦ http://static.springsource.org/spring/docs/3.1.x/s

pring-framework-reference/html/

Spring Testing◦ http://static.springsource.org/spring/docs/3.1.x/s

pring-framework-reference/html/testing.html

top related