Explicação detalhada do OCMock, uma estrutura comum para teste de unidade iOS

Índice

1. Teste de unidade

1.1 A necessidade de testes de unidade

1.2 Objetivo do teste de unidade

1.3 As duas estruturas principais das quais o teste de unidade depende

2. Integração e uso do OCMock

2.1 Método de integração OCMock

2.2 Como usar o OCMock

2.3 restrições de uso simulado

Três, finalmente

END Compartilhamento de Recursos de Aprendizagem de Apoio


1. Teste de unidade

1.1 A necessidade de testes de unidade

O desenvolvimento orientado a testes não é um conceito muito novo. No desenvolvimento diário, geralmente é necessário testar, mas esse tipo de saída é algo que deve ser exibido na tela após clicar em uma série de botões. Ao testar, você costuma usar o simulador para iniciar o aplicativo do zero repetidas vezes e, em seguida, localize o programa do módulo em que está, faça uma série de operações de clique e verifique se os resultados atendem às suas expectativas.

Esse comportamento é, sem dúvida, uma grande perda de tempo. Muitos engenheiros seniores descobriram que podemos construir uma cena semelhante no código e, em seguida, chamar o código que queremos verificar antes no código e comparar os resultados da execução com os resultados esperados no programa. Se eles forem consistentes, será mostra que não há nenhum problema com nosso código e, portanto, testes de unidade são gerados.

1.2 Objetivo do teste de unidade

O principal objetivo do teste de unidade é encontrar a lógica interna, a sintaxe, o algoritmo e os erros funcionais do módulo.

O teste de unidade é baseado principalmente no teste de caixa branca para verificar os seguintes problemas:

  • Verifique se o código corresponde ao design.
  • Encontre erros no projeto e nos requisitos.
  • Erros de pontos introduzidos durante a codificação.

O teste de unidade se concentra nas seguintes partes:

Caminho Independente - Testes para caminhos e loops básicos de execução, os possíveis erros são:

  • Comparação de diferentes tipos de dados.
  • "Erro Mistake 1", ou seja, pode ser um ciclo a mais ou um ciclo a menos.
  • Condição de rescisão falsa ou impossível.
  • Modificação inadequada de uma variável de loop.

Estruturas de dados locais - As estruturas de dados locais das células são a fonte mais comum de erros e os casos de teste devem ser projetados para verificar possíveis erros:

  • Tipos de dados inconsistentes.
  • Verifique se há tipos de dados incorretos ou inconsistentes.

Tratamento de erros - um projeto de unidade relativamente completo deve ser capaz de prever as condições de erro e configurar o tratamento de erros apropriado, de modo que, quando o programa cometer um erro, o erro possa ser reorganizado para garantir a correção da lógica do período:

  • A descrição do erro é difícil de entender.
  • O erro exibido não corresponde ao erro real.
  • Tratamento incorreto de condições de erro.

Condições de contorno - Erros nos limites são os sintomas de erro mais comuns:

  • Ocorreu um erro ao obter os valores máximo e mínimo.
  • As comparações de maior e menor no fluxo de controle geralmente são errôneas.

Interface da unidade - a interface é, na verdade, uma coleção de relações correspondentes entre entrada e saída. O teste dinâmico de uma unidade nada mais é do que fornecer uma entrada à unidade e, em seguida, verificar se a saída é consistente com as expectativas. Se os dados não puderem ser inseridos e enviados normalmente, o teste de unidade está fora de questão, então a interface da unidade precisa ser testada da seguinte forma:

  • Se o número, as propriedades e a sequência da entrada e saída da unidade em teste são consistentes com a descrição no projeto detalhado.
  • Se os parâmetros formais que são usados ​​apenas para entrada devem ser modificados.
  • Se as restrições são passadas por parâmetros formais.

1.3 As duas estruturas principais das quais o teste de unidade depende

O OCUnit (isto é, testar com XCTest) é, na verdade, a própria estrutura de teste da Apple, que é usada principalmente para asserção. Por ser simples de usar, este artigo não apresentará muito.

A principal função do OCMock é simular o valor de retorno de um método ou propriedade. Você pode se perguntar por que deveria fazer isso? Usar o objeto modelo gerado pelo modelo e depois passá-lo? A resposta é sim, mas existem casos especiais, como alguns objetos que não são fáceis de construir ou obter, neste momento você pode criar um objeto fictício para completar o teste. A ideia da implementação é criar um objeto correspondente de acordo com a classe do objeto a ser ridicularizado e definir as propriedades do objeto e as ações após chamar o método predeterminado (como retornar um valor, chamar um bloco de código, enviar uma mensagem , etc.) e, em seguida, registre-o em uma matriz, o desenvolvedor chama ativamente o método e, finalmente, executa uma verificação (verificação) para determinar se o método é chamado ou se uma exceção é lançada durante a chamada, etc. O uso de pontos mais difíceis no desenvolvimento de teste de unidade também é o uso pouco claro do OCMock Este artigo fala principalmente sobre a integração e uso deste OCMock.

2. Integração e uso do OCMock

2.1 Método de integração OCMock

O projeto integra a biblioteca de terceiros OCMock, que pode ser instalada diretamente com o framework OCMock usando a ferramenta pod. Se você usar a ferramenta iBiu para instalar a biblioteca OCMock, será necessário criar o Podfile.custom no mesmo nível do podfile.

Adicione o OCmock usando o mesmo formato de um arquivo pod normal da seguinte forma:

source 'https://github.com/CocoaPods/Specs.git'
pod 'OCMock'

2.2 Como usar o OCMock

(1) Método de substituição (stub): Informe ao objeto simulado qual valor retornar quando someMethod for chamado

Método de chamada:

d jalopy = [OCMock mockForClass[Car class]];
OCMStub([jalopy goFaster:[OCMArg any] units:@"kph"]).andReturn(@"75kph");

Cenas a serem usadas:

1. Ao verificar o método A, o método A usa o valor de retorno do método B internamente, mas a lógica interna do método B é mais complicada. Neste momento, o método stub precisa ser usado para fazer o stub do valor de retorno do método B. A implementação do código é semelhante ao código a seguir para implementar o valor de retorno fixo de funcB, para que os parâmetros que atendam aos requisitos de teste possam ser obtidos sem afetar o código-fonte.

método antes do stub

- (NSString *)getOtherTimeStrWithString:(NSString *)formatTime{
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateStyle:NSDateFormatterMediumStyle];
    [formatter setTimeStyle:NSDateFormatterShortStyle];
    [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; //(@"YYYY-MM-dd hh:mm:ss") ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制
    //设置时区选择北京时间
    NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
    [formatter setTimeZone:timeZone];
    NSDate* date = [formatter dateFromString:formatTime]; //------------将字符串按formatter转成nsdate
    //时间转时间戳的方法:
    NSInteger timeSp = [[NSNumber numberWithDouble:[date timeIntervalSince1970]] integerValue] * 1000;
    return [NSString stringWithFormat:@"%ld",(long)timeSp];
}

Depois de usar stub(mockObject getOtherTimeStrWithString).andReturn(@"1000") stub é semelhante ao seguinte efeito

- (NSString *)getOtherTimeStrWithString:(NSString *)formatTime{
    
    return @"1000";
    
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateStyle:NSDateFormatterMediumStyle];
    [formatter setTimeStyle:NSDateFormatterShortStyle];
    [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; //(@"YYYY-MM-dd hh:mm:ss") ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制
    //设置时区选择北京时间
    NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
    [formatter setTimeZone:timeZone];
    NSDate* date = [formatter dateFromString:formatTime]; //------------将字符串按formatter转成nsdate
    //时间转时间戳的方法:
    NSInteger timeSp = [[NSNumber numberWithDouble:[date timeIntervalSince1970]] integerValue] * 1000;
    return [NSString stringWithFormat:@"%ld",(long)timeSp];
}

2. O processo normal do código foi testado e é muito robusto, mas alguns processos errados não são fáceis de encontrar, mas podem existir, como dados de valor de borda, stubs podem ser usados ​​para simular os dados no teste de unidade e o código de teste é executado sob condições especiais de dados Condição.

Nota: stub() também não pode definir o valor de retorno, e a verificação é viável. O palpite pode ser o nil ou void retornado, então métodos sem valores de retorno também podem ser usados ​​para stubs de método.

(2) Existem atualmente três maneiras de gerar um objeto Mock.

Por meio do exemplo de teste do método talk da classe Person, que também envolve a classe Men e a classe Animaiton, a seguir estão os códigos-fonte relevantes das três classes.

classe de pessoa

@interface Person()
@property(nonatomic,strong)Men *men;
@end


@implementation Person
-(void)talk:(NSString *)str
{
    [self.men logstr:str];
    [Animaiton logstr:str];
    
}
@end

classe de porta

@implementation Men
-(NSString *)logstr:(NSString *)str
{
    NSLog(@"%@",str);
    return str;
}
@end

categoria Animaton

@implementation Animaiton
+(NSString *)logstr:(NSString *)str
{
    NSLog(@"%@",str);
    return str;
}
-(NSString *)logstr:(NSString *)str
{
    NSLog(@"%@",str);
    return str;
}
@end

Ao realizar um único teste no método talk, é necessário zombar da classe pessoa. A seguir, três métodos diferentes para gerar objetos fictícios. São apresentados os métodos de chamada e os cenários de uso dos três métodos. Finalmente, as vantagens e desvantagens de cada método também é apresentado.Feito uma tabela para facilitar a distinção.

Simulação agradável

O objeto fictício criado pelo NiceMock chamará primeiro o método de instância ao realizar o teste do método.Se o método de instância não for encontrado, ele continuará chamando o método de classe com o mesmo nome. Portanto, esse método pode ser usado para gerar objetos fictícios para testar métodos de classe e métodos de objeto.

Como usar:

- (void)testTalkNiceMock {
    id mockA = OCMClassMock([Men class]);
    Person *person1 = [Person new];
    person1.men = mockA;
    [person1 talk:@"123"];
    OCMVerify([mockA logstr:[OCMArg any]]);
}

Cenas a serem usadas:

Nice mock é mais amigável, quando um método sem stub é chamado, ele não causará uma exceção e passará na verificação. Se você não quiser stub muitos métodos sozinho, use bons mocks. No exemplo acima, quando mockA chama testTalkNiceMock, +(NSString *)logstr:(NSString *)str na classe Men não executará a operação de impressão. Durante o processo de chamada, como há logstr: métodos de classe e métodos de instância com o mesmo nome ao mesmo tempo, o método de instância será chamado primeiro.

Simulação Estrita

Como usar:

O caso de teste é o seguinte, mockA é gerado por Strict Mock e o método testTalkStrictMock é chamado, e o método testTalkStrictMock é chamado quando o Mock é gerado, então o método precisa usar um stub para o stub, caso contrário, o final OCMVerifyAll (mockA ) lançará uma exceção.

- (void)testTalkStrictMock {
    id mockA = OCMStrictClassMock([Person class]);
    OCMStub([mockA talk:@"123"]);
    [mockA talk:@"123"];
    OCMVerifyAll(mockA);
}

Cenas a serem usadas:

O objeto fictício criado dessa maneira lançará uma exceção se o método sem stub (stub significa stub) for chamado. Isso precisa garantir que todo método invocado independentemente seja interrompido durante o ciclo de vida do mock.Este método é estritamente usado e raramente usado.

Simulação Parcial

Quando o objeto criado desta forma chama o método: se o método for stub, chame o método após o stub, se o método não for stub, chame o método do objeto original, que é limitado ao objeto de instância simulada.

Como usar:

- (void)testTalkPartialMock {
    id mockA = OCMPartialMock([Men new]);
    Person *person1 = [Person new];
    person1.men = mockA;
    [person1 talk:@"123"];
    OCMVerify([mockA logstr:[OCMArg any]]);
}

Cenas a serem usadas:

Ao chamar um método que não é stub, o método do objeto real é chamado. Essa técnica é útil quando o stub de um método de uma classe não funciona bem. +(NSString *)logstr:(NSString *)str na classe Men executará a operação de impressão ao chamar testTalkPartialMock.

Tabela de diferenças de três vias:

(3) Chamada do método de verificação

Método de chamada:

OCMVerify([mock someMethod]);
OCMVerify(never(),    [mock doStuff]); //从没被调用
OCMVerify(times(n),   [mock doStuff]);   //调用了N次
OCMVerify(atLeast(n), [mock doStuff]);  //最少被调用了N次
OCMVerify(atMost(n),  [mock doStuff]);

Cenas a serem usadas:

No teste de unidade, você pode verificar se um método é executado e quantas vezes ele é executado.

Chamada de verificação atrasada:

OCMVerifyAllWithDelay(mock, aDelay);

Cenário de uso: Esta função é usada para esperar por operações assíncronas com mais frequência, onde aDelay é o tempo de espera mais longo esperado.

(4) Adicione expectativas

Método de chamada:

Preparar dados:

NSDictionary *info = @{@"name": @"momo"};
id mock = OCMClassMock([MOOCMockDemo class]);

Adicionar esperado:

OCMExpect([mock handleLoadSuccessWithPerson:[OCMArg any]]);

Pode-se esperar que não execute:

OCMReject([mock handleLoadFailWithPerson:[OCMArg any]]);

Os parâmetros podem ser validados:

// 预期 + 参数验证
OCMExpect([mock handleLoadSuccessWithPerson:[OCMArg checkWithBlock:^BOOL(id obj) {
    MOPerson *person = (MOPerson *)obj;
    return [person.name isEqualToString:@"momo"];
}]]);

A ordem de execução pode ser esperada:

// 预期下列方法顺序执行
[mock setExpectationOrderMatters:YES];
OCMExpect([mock handleLoadSuccessWithPerson:[OCMArg any]]);
OCMExpect([mock showError:NO]);

Os argumentos podem ser ignorados (quando a execução do método é esperada):

OCMExpect([mock showError:YES]).ignoringNonObjectArgs; // 忽视参数

implemento:

[MOOCMockDemo handleLoadFinished:info];

Afirmação:

OCMVerifyAll(mock);

Asserções podem ser atrasadas:

OCMVerifyAllWithDelay(mock, 1); // 支持延迟验证

O último OCMVerifyAll irá verificar se as expectativas anteriores são válidas, caso não seja chamada ocorrerá um erro.

(5) Restrições de parâmetros

Método de chamada:

OCMStub([mock someMethodWithAnArgument:[OCMArg any]])
OCMStub([mock someMethodWithPointerArgument:[OCMArg anyPointer]])
OCMStub([mock someMethodWithSelectorArgument:[OCMArg anySelector]])

Cenário de uso: Ao usar o método OCMVerify() para verificar se um método é chamado, o teste de unidade verificará se os parâmetros do método são consistentes. Se forem inconsistentes, ele avisará que a verificação falhou. Neste momento, se você apenas preste atenção na chamada do método e não preste atenção nos parâmetros, você pode usar [ OCMArg any] para passar parâmetros.

(6) Simulação de interface de rede

Como o nome sugere, ele pode zombar do retorno de dados da interface de rede para testar a direção e a precisão do código em dados diferentes.

Método de chamada:

id mockManager = OCMClassMock([JDStoreNetwork class]);
[orderListVc setComponentsNet:mockManager];
[OCMStub([mockManager startWithSetup:[OCMArg any] didFinish:[OCMArg any] didCancel:[OCMArg any]]) andDo:^(NSInvocation *invocation) {   


    void (^successBlock)(id components,NSError *error) = nil;   
    
    [invocation getArgument:&successBlock atIndex:3];  
    
    successBlock(@{@"code":@"1",@"resultCode":@"1",@"value":@{@"showOrderSearch":@"NO"}},nil);
    }];

O acima é chamar a interface dentro da chamada para o método setComponentsNet, que pode simular os dados de retorno necessários após chamar a interface e os dados de teste retornados no successBlock. Este método é obter a assinatura do método da chamada de interface, obter o parâmetro de passagem de retorno de chamada de sucesso successBlock e chamá-lo manualmente. Também é possível simular a falha da interface, desde que o callback de falha correspondente na assinatura seja obtido, ele pode ser realizado.

Cenário de uso: Ao escrever um método de teste de unidade, a simulação da interface de rede está envolvida e a interface simulada retorna o resultado dessa maneira.

(7) Restauração

Depois de substituir o método de classe, a classe pode ser restaurada ao seu estado original chamando stopMocking.

Método de chamada:

id classMock = OCMClassMock([SomeClass class]);
/* do stuff */
[classMock stopMocking];

Cenas a serem usadas:

Depois que o objeto de instância é substituído normalmente, stopMocking será chamado automaticamente depois que o objeto simulado for liberado, mas o objeto simulado adicionado ao método de classe abrangerá vários testes, o objeto de classe simulada não será desalocado após a substituição e o relacionamento simulado precisa ser cancelado manualmente.

(8) Simulação de observador - crie uma instância que aceite notificações

Método de chamada:

- (void)testPostNotification {   
Person *person1 = [[Person alloc] init];   
id observerMock = OCMObserverMock();   
//给通知中心设置观察者    
[[NSNotificationCenter defaultCenter] addMockObserver: observerMock name:@"name" object:nil];    
//设置观察期望    
[[observerMock expect] notificationWithName:@"name" object:[OCMArg any]];    //调用要验证的方法    
[person1 methodWithPostNotification];    
[[NSNotificationCenter defaultCenter] removeObserver:observerMock];    
// 调用验证   
OCMVerifyAll(observerMock);}

Cenas a serem usadas:

Crie um objeto fictício que possa ser usado para observar notificações. Mocks devem ser cadastrados para receber notificações.

(9) Protocolo simulado

Método de chamada:

id protocolMock = OCMProtocolMock(@protocol(SomeProtocol));
/*严格的协议*/
id classMock = OCMStrictClassMock([SomeClass class]);
id protocolMock = OCMStrictProtocolMock(@protocol(SomeProtocol));
id protocolMock = OCMProtocolMock(@protocol(SomeProtocol));
/*严格的协议*/
id classMock = OCMStrictClassMock([SomeClass class]);
id protocolMock = OCMStrictProtocolMock(@protocol(SomeProtocol));

Cenário de invocação: utilizado quando é necessário criar uma instância para ter as funções definidas pelo protocolo.

2.3 restrições de uso simulado

Para o mesmo método, não é possível stub primeiro e depois esperar: porque se você stub primeiro, todas as chamadas se tornarão stubs, portanto, mesmo que o método seja chamado pelo processo, a verificação OCMVerifyAll falhará no final; a solução é usar OCMExpect a propósito Stub, por exemplo: OCMExpect([mock someMethod]).andReturn(@"a string"), ou coloque o stub depois de esperar.

A zombaria parcial não se aplica a certas classes: como NSString e NSDate, essas classes de "ligação gratuita" lançarão exceções caso contrário.

Alguns métodos não podem ser stub: como: init, class, methodSignatureForSelector, forwardInvocation, etc.

Os métodos de classe de NSString e NSArray não podem ser stub, caso contrário, são inválidos.

As chamadas do método NSObject não podem ser validadas, a menos que sejam substituídas em uma subclasse.

As chamadas de método privado das classes principais da Apple não podem ser verificadas, como métodos que começam com _.

As chamadas de método de verificação atrasadas não são suportadas e apenas a verificação atrasada do modo expect-run-verify é suportada atualmente.

OCMock não suporta multithreading.

Três, finalmente

Espero que este artigo e exemplos tenham esclarecido alguns dos usos mais comuns do OCMock. A zombaria é monótona, mas necessária para uma aplicação. Se um método é difícil de testar com mocks, é sinal de que seu design precisa ser repensado.

Se o artigo for útil para você, lembre-se de curtir, marcar e adicionar atenção. Vou compartilhar alguns produtos secos de vez em quando...

END Compartilhamento de Recursos de Aprendizagem de Apoio

Finalmente:  Para retribuir aos fãs obstinados, compilei um tutorial completo de aprendizado em vídeo de teste de software para você. Se precisar, pode obtê-lo gratuitamente 【保证100%免费】

Documentação da entrevista de teste de software

Devemos estudar para encontrar um emprego bem remunerado. As perguntas da entrevista a seguir são os materiais de entrevista mais recentes de empresas de Internet de primeira linha, como Ali, Tencent e Byte, e alguns chefes da Byte deram respostas confiáveis. Termine este conjunto Os materiais da entrevista acreditam que todos podem encontrar um emprego satisfatório.

insira a descrição da imagem aqui

Como obter o conjunto completo de informações:

Acho que você gosta

Origin blog.csdn.net/IT_LanTian/article/details/131287604
Recomendado
Clasificación