Qual é o método de teste mais apropriado neste cenário?

5

Estou escrevendo alguns aplicativos Objective-C (para OS X / iOS) e atualmente estou implementando um serviço para ser compartilhado entre eles. O serviço destina-se a ser bastante auto-suficiente.

Para a funcionalidade atual que estou prevendo, haverá apenas um método que os clientes chamarão para executar uma série de etapas razoavelmente complicada, usando métodos privados na classe e passando dados por meio de várias classes de manipulação de dados. chegar a um resultado final.

A essência do código é buscar um log de alterações, armazenadas em um armazenamento de dados interno de serviço, que ocorreu desde um determinado momento, simplificar o log para incluir apenas a última alteração aplicável para cada objeto, anexar o serializado valores para os objetos afetados e retornar tudo isso para o cliente.

A minha pergunta é: como faço para testar o método de ponto de entrada? Obviamente, cada classe teria testes unitários completos para garantir que sua funcionalidade funcione como esperado, mas o ponto de entrada parece mais difícil de "desconectar" do resto do mundo. Eu prefiro não enviar cada uma dessas classes internas ao estilo IoC, porque elas são pequenas e são feitas apenas classes para satisfazer o princípio da responsabilidade única.

Eu vejo algumas possibilidades:

  1. Crie um cabeçalho de interface "privado" para os testes com métodos que chamam as classes internas e teste cada um desses métodos separadamente. Então, para testar o ponto de entrada, faça uma simulação parcial da classe de serviço com esses métodos privados ridicularizados e apenas teste que os métodos são chamados com os argumentos corretos.
  2. Escreva uma série de testes mais pesados para o ponto de entrada sem zombar de nada, testando toda a funcionalidade de uma só vez. Isso parece, para mim, mais parecido com "testes de integração" e parece frágil, mas satisfaz o princípio de "testar apenas através da interface pública".
  3. Escreva uma fábrica que retorne esses serviços internos, coloque-a no inicializador e, em seguida, escreva uma fábrica que retorne versões ridicularizadas para usar nos testes. Isso tem a desvantagem de tornar a construção do serviço irritante e vaza detalhes internos para o cliente.
  4. Escreva um inicializador "privado" que use esses serviços como parâmetros extras, use isso para fornecer serviços falsos e tenha o back-end do inicializador público para este. Isso garantiria que o código do cliente ainda visse o inicializador fácil / bonito e não haver vazamentos internos.

Tenho certeza de que há mais maneiras de resolver esse problema em que ainda não pensei, mas minha pergunta é: qual é a abordagem mais apropriada de acordo com as práticas recomendadas de teste de unidade? Especialmente considerando que eu preferiria escrever este teste em primeiro lugar, significando que eu deveria, de preferência, criar apenas esses serviços, pois o código indica uma necessidade para eles.

    
por Daniel Bruce 24.04.2013 / 10:45
fonte

2 respostas

2

Existe alguma lógica importante no seu método de "ponto de entrada"? Parece que seu único trabalho é instanciar algumas outras classes e conectá-las. Se isso é certo, então eu diria que este é um código trivial, não é crítico para os negócios, você notaria imediatamente se ele estivesse quebrado (geralmente se você errar este tipo de código de 'composição' ele nem compilará), então - Eu sei que isso parece blasfêmia - não precisa de testes unitários . "O melhor código não é código" também se aplica a testes: se um teste não está testando algo importante, difícil ou passível de quebra sem detecção, então esse teste não vale o esforço de escrever ou manutenção.

Se o método main for mais do que apenas um código trivial ( try...catch blocos são comuns nesse tipo de método, assim como if instruções para configuração) então você deve provavelmente considerar extrair alguma lógica. Por exemplo, se você tiver muitas instruções if em seu aplicativo, talvez possa extrair uma classe Configuration que lide com configurações arbitrárias e que possa ser facilmente testada ou estendida na unidade.

    
por 27.05.2014 / 00:17
fonte
1

Eu faria algo como sua versão 4, mas tornaria esse inicializador público.

@interface Frobulator : NSObject

- (instancetype)init; //uses default collaborators
- (instancetype)initWithThingifier: (id <Thingifiying>)thingifier doohicker: (id <Doohicking>)doohicker; //designated initialiser

- (NSData *)frobulate: (NSData *)unfrobulatedData;

@property (nonatomic, readonly) id <Thingifying>thingifier;
@property (nonatomic, readonly) id <Doohicking>doohicker;

@end

Agora, sua unidade testa:

  • teste que -frobulate: interage com os colaboradores definidos através do inicializador. Estas podem ser brincadeiras.
  • teste que -init configura os colaboradores reais.

Você, então, tem a opção em seu aplicativo de usar o método -init para obter os colaboradores padrão ou fornecer diferentes colaboradores caso suas necessidades mudem.

    
por 01.05.2013 / 11:54
fonte