Python retorna o objeto MagicMock em vez de return_value

86

Eu tenho um arquivo python a.pyque contém duas classes Ae B.

class A(object):
    def method_a(self):
        return "Class A method a"

class B(object):
    def method_b(self):
        a = A()
        print a.method_a()

Eu gostaria de fazer um teste de unidade method_bem classe Bzombando A. Aqui está o conteúdo do arquivo testa.pypara esta finalidade:

import unittest
import mock
import a


class TestB(unittest.TestCase):

    @mock.patch('a.A')
    def test_method_b(self, mock_a):
        mock_a.method_a.return_value = 'Mocked A'
        b = a.B()
        b.method_b()


if __name__ == '__main__':
    unittest.main()

Espero obter Mocked Ana saída. Mas o que eu consigo é:

<MagicMock name='A().method_a()' id='4326621392'>

Onde estou fazendo errado?

Mehdi Jafarnia Jahromi
fonte
1
Ao testar, A()retorna o return_valuefrom mock_A(um regular MagicMock, já que você não especificou mais nada), que não é uma instância da classe A. Você precisa definir isso return_valuepara ser algo definido method_a.
jonrsharpe
3
mock_a.method_a.return_value = 'Mocked A' => mock_a (). method_a.return_value = 'Mocked A' deve ser melhor :)
Ali DISSE OMAR
@AliSAIDOMAR está precisamente correto, é o valor de retorno da chamada mock_aque deve ter o método, não mock_aele mesmo.
jonrsharpe
1
@jonrsharpe. Obrigado pela sua explicação. Eu apenas tentei. Ambos mock_a().method_a.return_value = 'Mocked A'e mock_a.return_value.method_a.return_value = 'Mocked A'funcionaram. Muito obrigado por seus comentários. Você poderia ir em frente e colocar isso como uma resposta?
Mehdi Jafarnia Jahromi
@MehdiJafarniaJahromi muito obrigado!
Niakros

Respostas:

96

Quando você @mock.patch('a.A'), está substituindo a classe Ano código em teste com mock_a.

Em B.method_bvocê, então a = A(), definir , que é agora a = mock_a()- ou seja, aé o return_valuede mock_a. Como você não especificou esse valor, é um normal MagicMock; isto também não está configurado, então você obtém a resposta padrão (ainda outra MagicMock) ao chamar métodos nele.

Em vez disso, você deseja configurar o return_valuedemock_a para ter o método apropriado, que pode ser:

mock_a().method_a.return_value = 'Mocked A' 
    # ^ note parentheses

ou, talvez mais explicitamente:

mock_a.return_value.method_a.return_value = 'Mocked A'

Seu código teria funcionado no caso a = A(atribuindo a classe, não criando uma instância), pois então a.method_a()teria acionado seu método mock.

Jonrsharpe
fonte
4
Impressionante. Você fez meu dia.
Vishal
Olá @jonrsharpe, estou usando um dataframe do pandas com df.columns para verificar minha condição if . Ele não usa parênteses (ou seja, não pode ser chamado). O que devo fazer para retornar uma lista nesse caso. Obrigado!
imsrgadich
@imsrgadich você precisa de um mock para isso? Basta construir o dataframe apropriado e tratá-lo como um valor de teste.
Jonrsharpe
@jonrsharpe sim, eu poderia fazer isso, mas também estou executando df.drop em meu método chamado, que preciso afirmar e sobre isso não estou retornando o dataframe de volta do método chamado. Isso cria um problema. Eu encontrei uma maneira de mock_data.configure_mock(columns='my_column')resolver isso. Obrigado pela resposta embora. (ref: bradmontgomery.net/blog/how-world-do-you-mock-name-attribute )
imsrgadich
Isso não parece funcionar quando você simula dois modelos SQLAlchemy no mesmo teste. O primeiro funciona bem, mas o segundo retornará um MagicMock independentemente do que você definir.
Juha Untinen