• Introdução
  • unittest.,simulação ou simulação
  • Decorador
  • localização de Recursos
  • Simulação de return_value vs side_effect
  • Simulação de Chamadas Aninhadas
  • Verifique Exceções
  • Limpeza de lru_cache
  • Simulação de Nível de Módulo/Variáveis Globais
  • Simulação de Método de Instância
  • Simulação de Método de Classe
  • Simulação de Toda a Classe
  • Simulação de Chamadas Assíncronas
  • Simulação de Tipos de Instância
  • Simulação de builtin open função
  • Conclusão

Introdução

Zombando de recursos ao escrever testes em Python pode ser confuso se você não estiver familiarizado com o fazer tais coisas., Neste post eu vou cobrir vários aspectos do Código zombing, que esperamos ser um recurso útil para aqueles que estão um pouco presos.

nota: nos exemplos de código estou usando o pytest, mas na maioria das vezes isso não deve importar.

unittest.mock ou mock

a fim de ‘mock’ um recurso Vamos primeiro precisar do mock módulo, e este é o nosso primeiro obstáculo: de que versão precisamos? por exemplo, há dois e ambos parecem ser oficiais (mock e unittest.mock).,

mock módulo é compatível biblioteca você pode fazer o download do PyPy, onde unittest.mock é a mesma coisa, mas só é compatível com a versão do Python que você está usando.,

Então, em quase todos os casos, você vai querer importar-lo assim:

import unittest.mock as mock

Para mais exemplos, consulte este guia de referência

Decorador

A maneira mais comum para zombar de recursos é a utilização de um Python decorador em torno de sua função de teste:

@mock.patch("thing")def test_stuff(mock_thing): mock_thing.return_value = 123

neste caso, o que estamos a aplicação de patches (thing) pode ser uma variável ou uma função.,

Quando fizer isto, terá de passar um argumento para a sua função (pode dar-lhe o nome que quiser †) que será um MagicMock.

isto significa que se não fizer mais nada, então chamadas para thingirá (no exemplo acima pelo menos) resultar no valor123 a ser devolvido.

† convention is to name the variablemock_<noun>.,

Se você está zombando de várias coisas, então você vai empilhar o mock decoradores sobre o outro, e passá-las junto para a função de teste:

@mock.patch("third")@mock.patch("second")@mock.patch("first")def test_stuff(mock_first, mock_second, mock_third): ...

localização de Recursos

É importante saber que quando zombam de você deve especificar a localização do recurso a ser escarnecido, relevantes para onde é importado., Isso é melhor explicado por meio de exemplo…

Imagine que eu tenho um módulo app.foo e dentro do módulo I de importação outra dependência da seguinte forma:

from app.bar import thing

Você pode pensar que quando você chamar mock.patch a passagem de uma referência para o recurso, como app.bar.thing. Isso só seria relevante se o recurso estivesse sendo chamado com esse caminho completo dentro do módulo

(por exemplo, seapp.foochamadoapp.bar.thing(...)).,

Se a localização do espaço de nomes completo não for referenciada, o que não está no exemplo acima (aviso que importamos apenas o id

recurso). Isso significa que precisamos para especificar a referência de espaço de nomes para simulação de onde é importado:

@mock.patch('app.foo.thing')

Então, mesmo que thing existe dentro de app.bar temos que especificar app.foo.thing app.foo é onde temos de importados para uso. Isto apanha as pessoas a toda a hora.,

Simulação de return_value vs side_effect

Se a função tem um try/except em torno dele, em seguida, você pode usar side_effect para causar a chamada da função para disparar uma Exceção, pois o valor retornado:

@mock.patch('app.aws.sdk.confirm_sign_up', side_effect=Exception('whoops'))

Nota: se você tivesse usado o return_value=Exception('whoops'), em seguida, a simulação seria voltar a representação de seqüência de caracteres de Exceção em vez de disparar uma exceção, como side_effect não.,método em um escarnecido objeto foi chamado:

A razão, este pode tornar-se mais complicado é devido à forma como a simulação de uma retornam uma nova simulação quando aceder a uma propriedade em uma brincadeira:

O código acima irá erro:

AssertionError: expected call not found.Expected: listen(8080)Actual: listen(123)

Você precisará certifique-se de que você afirma que a simulação no tempo certo:

Verificar Exceções

Se queremos verificar que algum pedaço de código lança uma Exception tipo quando precisamos que nós podemos zombar recursos específicos para lançar uma exceção e, em seguida, use pytest.raises como contexto o gestor de todo o chamador do nosso código para ser verificado.,

Podemos pegar e fazer afirmações contra este comportamento esperado zombando primeiro do recurso que queremos lançar uma exceção e fazê-lo lançar a nossa própria exceção falsa usando o parâmetro

.

em seguida, especificar exatamente o tipo de exceção que estamos esperando para ser criado usando pytest.raises(T):

Nota: não cometa o erro de colocar quaisquer afirmações dentro de with gestor de contexto., Uma vez que a exceção é levantada pela função sendo chamada dentro do with Gerenciador de contexto, todo o código após ele dentro do bloco é ignorado.

Limpeza de lru_cache

Se uma função que você deseja testar e tem o functools.lru_cache decorador aplicada, em seguida, você precisará estar ciente de zombar da resposta da função de como ele vai ser armazenados em cache em um teste e o resultado em cache será devolvido ao chamar a função novamente para testar alguns outros comportamentos (e talvez provável confundir você quando você ver a resposta inesperada).,

Para corrigir esse problema é muito fácil, pois lru_cache fornece funções adicionais quando decoratoring suas funções, fornece:

  • cache_info
  • cache_clear

O último (cache_clear) é o que você precisa chamar. Isto é demonstrado abaixo:

Nota: depuração isto nem sempre é óbvio., Mais tarde, eu demonstro como zombar da função builtin open, e nesse cenário eu tropecei nesta questão, porque embora eu não estivesse zombando da função de nível superior em si (eu estava zombando da chamada para open within), o conteúdo do arquivo que estava sendo aberto foi o que foi devolvido e sendo Cache.

Mock Module Level/Global Variables

With a module variable you can either set the value directly or use mock.patch.,

No exemplo a seguir temos a variável client_id que é uma variável global dentro de app.aws módulo que fazemos a importação de referência em outros lugares em nosso código:

mock.patch exemplo, há duas coisas importantes a observar:

  1. não usamos return_value.
  2. não há instância mock passada para a função de teste.,

Isto é porque estamos modificando uma variável, e não uma função direta ou ‘acessível’, de modo que não há necessidade de passar por uma simulação para a função de teste (se você deseja alterar o valor de algumas vezes dentro do próprio teste, em seguida, você iria zombar da variável, mas não imediatamente atribuir um valor em o decorador).

Mock Instance Method

There are multiple ways to achieve zombing of an instance method., Uma abordagem comum é usar mock.patch.object, assim:

Outra abordagem é o método de simulação como você faria com uma função normal, mas você faz referência o método através do classname:

Outro (apesar de mais pesada) abordagem por ridicularizar a uma instância da classe método é aproveitar o fato de que uma Simulação irá retornar uma nova simulação de instância quando chamado:

@mock.patch("foo.bar.SomeClass")def test_stuff(mock_class): mock_class.return_value.made_up_function.return_value = "123"

Nota: no exemplo acima temos a zombar de toda a classe, que pode não ser o que você quiser. Se não, então use o exemplo anterior

.,

A razão, o exemplo acima funciona é porque estamos a definição de return_value na nossa maquete. Porque este é um MagicMock cada atributo referenciado retorna uma nova simulação (instância de uma função ou propriedade chamada em uma brincadeira não tem de existir) e por isso nós o chamamos de made_up_function no devolvidos simulação, e em que o recém-criado zombar de nós definir o final return_value 123.,

mas como mencionado na nota acima, esta abordagem pode ser um pouco brusca demais dependendo do que suas necessidades são (se você se importa se você tem alguma classe funcional ou não).

Mock Class Method

to mock a class method is a similar approach to mocked an instance method.,

Uma abordagem pode ser que você zombar de toda a classe (mas agora você tem um a menos return_value atribuir ao):

mock_class.ClassMethodName.return_value = "123"

Ou melhor ainda, você deve mock-lo como faria com qualquer função normal, mas apenas referência, o método através da classe:

Zombar de Toda a Classe

A zombar de toda uma classe, você precisará definir o return_value para ser uma nova instância da classe.,

Ver outras classes relacionadas zombando dicas aqui

Simulação de Chamadas Assíncronas

Zombando de código assíncrono é, provavelmente, o mais confuso aspecto de simulação. Minha solução’ ir para ‘ eu vou explicar primeiro, mas depois disso eu vou compartilhar alguns métodos alternativos que eu vi e tentei no passado.,

Primeiro, considerar este código assíncrono dentro de uma app.foo módulo:

import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)

Se a gente precisa de simulação a co-rotina app.stuff.some_concurrent_function, então podemos resolver isso criando uma função que funciona como uma co-rotina e permitir que ele seja configurável para diferentes tipos de respostas:

Nota: o exemplo usa tornado para a execução assíncrona de teste.,e alternativas…

AsyncMock

Nota: este utiliza o pacote pytest-asyncio para ajudar com o teste de asyncio código

Vamos começar com o código para ser escarnecido…

import asyncioasync def sum(x, y): await asyncio.sleep(1) return x + y

Ora aqui está como gostaríamos de simulação de…

Monkey Patch

MagicMock Subclasse

Assíncrono de Função Inline

Simulação de Tipos de Instância

Quando zombando de um objeto, você verá que a simulação substitui o objeto inteiro e por isso pode causar testes para passar (ou não) de formas inesperadas.,

ou seja, se você precisa fazer uma simulação mais como o concreto interface e, em seguida, há duas maneiras de o fazer:

  1. spec
  2. wrap

podemos usar mock spec recurso para imitar todos os métodos/atributos do objeto que está sendo ridicularizado. Isso garante que suas piadas têm a mesma api que os objetos que estão substituindo.

Nota: existe um mais rigoroso spec_set que irá aumentar um AttributeError.,

isto é melhor demonstrado com um exemplo:

O parâmetro wrap por outro lado, permite-lhe “espiar” a implementação, bem como afectar o seu comportamento.,no que zomba do objeto como um todo, e não apenas um único método:

Simulação de builtin função open

Python irônica biblioteca fornece uma abstração para ridicularizar o builtin open função muito simples…

create=True param definido no mock.patch significa que o mock.MagicMock voltou irá automaticamente criar os atributos que são chamados a simulação (isto é porque o open função irá tentar acessar um monte de coisas diferentes, e é mais fácil de simulação para simulação de fora de tudo isso para você).,conclusão lá vamos nós acabar. Espero que esta lista de técnicas de zombaria será capaz de vê-lo através até mesmo o mais complexo de código que precisa ser testado. Diz-me o que achas no twitter.