Sem categoria

Como testar tudo

[Esta postagem também aparece em Blog do Dustin no github].

Recentemente, tive um Membase o usuário aponta uma sequência de operações que levou a um estado indesejável. Tenho muitos testes de mecanismo realmente bons que escrevi, mas não este caso:

adicionar com tempo limite -> esperar pelo tempo limite -> adicionar com tempo limite

O erro é bastante simples: a expiração é preguiçosa e acontece que não estou verificando a expiração nesse caso. Foi muito fácil

escrever esse teste, mas imediatamente me fez pensar sobre o que outros casos não estavam sendo executados.

Agora, sei que existem inúmeras ferramentas para ajudar nos testes. Eu já escrevi outra. Provavelmente passei cerca de uma hora escrevendo uma estrutura para escrever e executar todos os testes de que precisava. A diferença entre o que estou descrevendo aqui e, por exemplo, verificação rápida é que eu quero algo muito simples para expressar ações que esperam que o ambiente esteja em um determinado estado e que deixará o ambiente em outro estado. Em seguida, quero atingir todos os arranjos possíveis dessas ações para garantir que elas não interfiram umas nas outras de forma inesperada.

Permutations

Isso aumenta muito rapidamente - especificamente o número de testes gerados para uma sequência de testes de n ações de a ações possíveis é de aproximadamente an.

Considere três ações definidas permutadas em sequências de duas. Isso resulta em nove possibilidades, conforme mostrado no diagrama à direita.

As ações no diagrama são definidas com a semântica do memcached em uma única chave, portanto adicionar tem como pré-requisito que o item não deve existem e del tem como pré-requisito que o item deve existem.

O teste gerado espera sucesso em cada caixa branca, falha em cada caixa vermelha e rastreia as mutações de estado esperadas para criar asserções.

BreakDancer Growth

Meu primeiro teste... hum, teste realizado com 11 ações em sequências de 4 ações. Tenho mais ações a realizar, mas 4 é um comprimento muito bom, portanto, o gráfico à esquerda demonstrará minha taxa de crescimento.

O mais impressionante é que ele apontou o bug original com bastante facilidade e outros dois bugs com pouco esforço.

Como posso usar isso?

action lifecycle

Até o momento, a API é bastante simples e compostável. Há basicamente cinco classes (três são mostradas na imagem à direita).

Condição

A Condição é um chamável simples que é usado para pré-condições e pós-condições. Uma determinada classe não se importa com qual delas é usada e, em muitos casos, será usada para ambas.

Por exemplo, considere minha implementação de Não existe:

classe Não existe(Condição):
def __call__(autônomo, estado):
retorno TESTKEY não em estado

Efeito

Um Efeito altera nossa visão do estado (e, dependendo do driver, pode realmente fazer com que algo no mundo mude com ele). Por exemplo, o StoreEffect funciona da seguinte forma:

classe StoreEffect(Efeito):
def __call__(autônomo, estado):
estado[TESTKEY] = ‘0’

Ação

Um Ação traz um Efeito e um ou mais Condição como condições pré e pós. Por exemplo, examinaremos duas ações, uma Adicionar ação e um Conjunto ação:

classe Set(Action):
efeito = StoreEffect()
postconditions = [Exists()]

class Add(Action):
condições prévias = [DoesNotExist()]
efeito = StoreEffect()
postconditions = [Exists()]

A parte interessante disso é que Set e Add têm semântica diferente, mas são expressos como composições diferentes das mesmas condições e efeitos.

Motorista

O driver é uma parte maior (sete métodos definidos!). Ele faz o suficiente para que eu possa fazer qualquer coisa, desde gerar um conjunto de testes em C para mecanismos memcached até realmente executar testes em um protocolo remoto.

Não descreverei todo o processo aqui, pois ele está documentado no fonte. No entanto, fecharei o ciclo mostrando um exemplo de código gerado por ele que demonstra o erro que não conseguimos encontrar em primeiro lugar:

static enum test_result test_add_add_delay_add(ENGINE_HANDLE *h,
ENGINE_HANDLE_V1 *h1) {
adicionar(h, h1);
assertHasNoError(); // valor é “0”
adicionar(h, h1);
assertHasError(); // valor é “0”
atraso(expiração+<extensão classe="mi">1);
assertHasNoError(); // valor é não definido
adicionar(h, h1);
assertHasNoError(); // valor é “0”
checkValue(h, h1, “0”);
retorno SUCESSO;
}
</span>

Isso demonstra a quantidade de informações que você conhece em cada etapa do caminho. A partir daí, podemos fazer todo tipo de coisa com nossos stubs (o atraso acima é implementado com o recurso de "viagem no tempo" do memcached testapp, por exemplo).

A partir daí, é menos empolgante. Nós fornecemos restrições, ele escreve testes e garante que haja outra área em que seja impossível para os usuários encontrarem algo que não tenhamos visto antes.

Compartilhe este artigo
Receba atualizações do blog do Couchbase em sua caixa de entrada
Esse campo é obrigatório.

Autor

Postado por Dustin Sallings

Dustin Sallings é arquiteto-chefe da Couchbase. Dustin é autor do spymemcached e um dos principais colaboradores do Couchbase e do <a href="https://developer.couchbase.com/documentation/server/3.x/developer/dev-guide-3.0/memcached.html#projects">Projetos do Memcached.</a>

Deixe um comentário

Pronto para começar a usar o Couchbase Capella?

Iniciar a construção

Confira nosso portal do desenvolvedor para explorar o NoSQL, procurar recursos e começar a usar os tutoriais.

Use o Capella gratuitamente

Comece a trabalhar com o Couchbase em apenas alguns cliques. O Capella DBaaS é a maneira mais fácil e rápida de começar.

Entre em contato

Deseja saber mais sobre as ofertas do Couchbase? Deixe-nos ajudar.