Depois de ficar maravilhado com as promessas de um mundo melhor código mais harmonioso, manutenível e desacoplado, vem uma curta pergunta que nos leva a nossa primeira Rua sem Saída: como fazer meu primeiro teste (do projeto) passar?
As minhas Ruas sem saída foram: "em um projeto novo, o que testar primeiro?" e, "em um projeto legado como fazer o primeiro teste?"
Vou criar um cenário hipotético, apenas para fins de apoio: você é contratado para fazer uma Extranet de Bike Shop. Os requisitos em alto nível são:
- Área segura para somente funcionários/gestores logarem no sistema
Somente após login, a aplicação liberará acesso às features:
- Cadastro de Marcas e Modelos de bicicleta;
- Cadastro de bicicleta;
- Cadastro de peças de reposição para uma (ou várias) marcas de bicicletas. - Pense nessas peças como peças de um determinado fabricante de carro: você pode comprar a peça "original" com a logo do fabricante do carro na peça ou a "paralela" feita pelo mesma empresa (por ex. Cofap, de amortecedores) mas sem a logomarca de nenhuma fabricante de carro, em um CarShop qualquer de rua.
- Cadastro de peças genéricas (a tal da paralela).
- Listagem de tudo isso que foi pedido.
Para efeitos de exemplos:
Marca: Caloi Modelo: Urbe Peça de reposição: Guidão Flat Peça genérica: Cambio Traseiro Shimano TX 7 Velocidades
A peça genérica em nosso bike shop, em tese servirá para qualquer bicicleta que precisar daquele componente.
Começando: você escolhe a linguagem de programação, um possível bootstrap para subir um Hello Test no navegador e pronto. Está pronto para começar a fazer as coisas. Mas, e agora?
Sabendo por onde começar
A pergunta inicial não deveria ser "Por onde começar?" mas sim, "O que é mais importante no projeto?". Seu cliente/chefe irá ver o projeto de tempos em tempos e você certamente terá dúvidas que só ele saberá responder. Enquanto o projeto não está pronto, o que é dispensável no projeto?
O sistema de autenticação é claro! Incrivelmente com todos que falei ou fiz essa pergunta (incluindo eu mesmo - sim, falo sozinho), o sistema de autenticação é escolhido como primeira coisa a ser feita. Pense bem. Para que raios a autenticação é importante agora? Ela evitará acesso indevido, mas se o software está em processo embrionário, com acesso limitado ao servidor e dados dummies, para que ele serve? Ignore-o por enquanto e foque no core do negócio!
Os pingos nos ís
Nat Price, sugeriu em seu livro criar um Teste de Aceitação e com este, criar seu primeiro Teste Unitário de Unidade. Seguiria criando os testes de unidade fazendo-os passar até que o teste de aceitação passaria também, finalizando assim aquela feature.
Eu já fiz essa abordagem, mas achei ela um tanto verbosa em casos onde o do teste de aceitação é muito parecido com o teste de unidade - até porque um software web é arquitetonicamente diferente de um software de missão crítica ou embarcado, por exemplo.
Atualmente, eu sigo apenas o processo interno, sem Teste de Aceitação ou/e Integração pelo simples fato de ser mais direto e não há nenhum efeito colateral no processo em si.
O Primeiro Green, Like a Boss!
Eu começaria pela Peça. Uma classe para identificar Peças "Originais" e outra para Peças Genéricas ou apenas uma?
O legal é esse tipo de decisão, pode ser adiado com Test-Driven Development. Vamos começar pelo mais fácil: uma classe Peça.
@test
everyGenuinePartBelongsToABicycle()
Assim como em Concessionárias de automóveis, há peças que são relacionadas ao veículo e por mais que sirva em mais de um modelo, a relação Parte-Veículo torna-se indispensável.
Algo assim, pode ser um começo:
class BicyclePartTest {
private subject = new Part();
private bicycle = a_bicycle_mock_object;
@test
public everyGenuinePartBelongsToABicycle() {
subject.setBicycle(bicycle)
assertTrue(subject.isGenuine())
}
}
Ou seja, para Part (Peça da bike) ser Genuína (Original), a peça precisa estar relacionada a uma bike em específico. Esse simples teste já nos guia para nosso próximo:
class BicycleTest {
private subject = new Bicycle();
@test
public willContainGenuineParts() {
subject.addGenuinePart(a_mocked_part)
assertCount(1, subject.getGenuineParts())
}
}
Uma bicicleta deverá ter Peças. Neste caso, Peça Genuína devemos atentar que a relação é uma Composição, ou seja, a Parte(Part) não existe sem o Todo (Bicycle). Já sabemos também que:
class Bicycle {
public void addGenuinePart(Part genuinePart) {
parts.add(genuinePart)
genuinePart.setBicycle(this)
}
}
A relação bi-direcional deverá existir, conforme o teste do BicyclePart sugeriu lá no começo.
Montando a classe Part para que satisfaça o teste do BicyclePart, você já verá o Green na tua tela. Mais do que um, dois pontos verdes.
Eu já consigo ter uma próxima dúvida: em peças genuínas, poderá haver mais de uma peça por bike? Sim, não?
Sim! Pode-se optar por quantificar quantas peças daquela a bike tem e aonde elas ficam:
class BicyclePartTest {
private subject = new Part();
private bicycle = a_bicycle_mock_object;
@test
public genuinePartMustHaveQuantityAndPosition() {
subject.setBicycle(bicycle);
subject.definePositionAndQuantity("front", 2)
subject.definePositionAndQuantity("rear", 2)
assertEquals(4, subject.getRequiredQuantity())
assertEquals(["front", "rear"], subject.getPositions())
}
@test
public everyGenuinePartBelongsToABicycle() { ... }
}
Implementando o código de produção, o teste passará mais uma vez. 3 greens!
E Part não Genuína? Também terá posicionamento e quantidade? Uma porca do Cubo da roda, por exemplo são duas na frente (direita e esquerda) e duas atrás (direita e esquerda). Então:
class BicyclePartTest {
private subject = new Part();
private bicycle = a_bicycle_mock_object;
@test
public partMustHaveQuantityAndPosition() {
subject.definePositionAndQuantity("front", 2)
subject.definePositionAndQuantity("rear", 2)
assertEquals(4, subject.getRequiredQuantity())
assertEquals(["front", "rear"], subject.getPositions())
}
@test
public everyGenuinePartBelongsToABicycle() { ... }
}
Neste caso nem precisei fazer outro teste. Apenas mudei o input e nome do teste anterior. Agora o teste diz que uma Part precisa ter quantidade e posição. Ok, mas e se eu não a definir? Uma vez que é obrigatório (..) uma ideia é mover para o construtor de Part, não? A simples ideia de fazer por steps, nos faz pensar sobre o negócio que estamos criando e consequentemente, criar um código mais legível e coeso (harmonioso, por exemplo).
Mas, mas...
E o Database Schema? A herança do ActiveRecord ou o Repository do DataMapper? Uma coisa que eu faço - até com Rails - é criar POJO's/PORO's/POPO's - Plain Old (Java | Ruby | PHP) Objects. depois que eu tenho uma relação mínima feita, daí sim eu crio as relações, os schemas, etc - pois eu sei que precisarei isolá-los e etc.
A autenticação ficou para uma fase mais madura do projeto. Poderá ser até mais para o fim, quando realmente ela for útil.
Concluíndo
Seguir o de sempre e começar sempre pelo mesmo lugar não é algo justo de ser feito. Talvez você precise de uma Introspecção para rever algumas práticas (..)
Criar software orientado a testes é muito divertido, você precisa tentar, sério!
Challenge
Se você quer tentar, pegue esse simples projeto que citei aqui e tente implementá-lo em com sua linguagem do momento. Se quiser, pode me enviar por Github para te dar umas dicas. Se quiser também, podemos marcar uma conversa para discutirmos o design da aplicação. Será muito valioso para todos participantes. ;)
0sem comentários ainda