【问题标题】:Mock an entire package with pytest and pytest-mock使用 pytest 和 pytest-mock 模拟整个包
【发布时间】:2015-05-15 00:30:57
【问题描述】:

我有一个使用包tmdbsimple的类:

电影.py

import tmdbsimple as tmdb

tmdb.API_KEY = '12345'

class Movie():
    def __init__(self, tmdb_id):
        movie = tmdb.Movies(tmdb_id)
        response = movie.info()

        self.tmdb_id = tmdb_id
        self.title = movie.title
        self.vote_average = movie.vote_average
        self.watched = False
*snip*

这一切都很好,直到我想在不依赖与 TMDb 的实际连接的情况下对其进行测试:

test_movie.py

import pytest

import movie
from fake_tmdbsimple import FakeTmdbsimple

@pytest.fixture
def testMovie(mocker):
    mocker.patch.dict('sys.modules', {'tmdbsimple': FakeTmdbsimple()})
    return movie.Movie('1')

def test_creation_of_Movie(testMovie):
    assert isinstance(testMovie, movie.Movie)

def test_properties_of_Movie(testMovie):
    assert testMovie.tmdb_id == 1
    assert testMovie.title == 'Example Movie'
    assert testMovie.vote_average == 10
    assert testMovie.watched == False

fake_tmdbsimple.py

class FakeTmdbsimple():
    def __init__(self):
        pass

    class Movies():
        _example_movie_data = {
            1: {
                'title': 'Example Movie',
                'vote_average': 10.0
            }
        }

        def __init__(self, tmdb_id):
            self.tmdb_id = tmdb_id

        def info(self):
            self.title = self._example_movie_data[self.tmdb_id]['title']
            self.vote_average = self._example_movie_data[self.tmdb_id]['vote_average']

    class Find():
        def __init__(self, identifier):
            pass

        def info(self, external_source):
            return {
                'movie_results': [
                    {'id': 1}
                ]
            }

    class Search():
        def __init__(self):
            pass

        def movie(self, query):
            return {
                'results': [
                    {
                        'id': 1
                    }
                ]
            }

当测试运行时,我得到这个错误:

*snip*
self = <tmdbsimple.movies.Movies object at 0x110779cf8>

    def __init__(self):
>       from . import API_VERSION
E       ImportError: cannot import name 'API_VERSION'

测试仍在尝试从真正的tmdbsimple 导入!在调用 tmdb.Movies() 时,testMovie 对象仍会收到 tmdbsimple.movies.Movies 的实例,而不是 fake_tmdbsimple.Movies。我相对确定这是我的“不是嘲笑对象在哪里使用,而是从哪里来”的结果。但是,在这种情况下,使用pytestpytest-mock,我不知道如何在需要的地方进行模拟。

我这样做是否正确?如果是这样,我需要做什么才能成功模拟整个 tmdbsimple 包?如果不是,那么在不实际访问 TMDb 的情况下测试我的 Movie() 类的正确方法是什么?

【问题讨论】:

    标签: python mocking pytest


    【解决方案1】:

    我之前有一个可行的实现,如下所示:

    test_movie.py:

    import pytest
    
    from pycoin import movie
    
    @pytest.fixture
    def testMovie(fake_tmdbsimple):
        return movie.Movie('1')
    
    @pytest.fixture
    def fake_tmdbsimple(monkeypatch):
        monkeypatch.setattr('tmdbsimple.Movies', FakeTmdbsimpleMovies)
        monkeypatch.setattr('tmdbsimple.Find', FakeTmdbsimpleFind)
        monkeypatch.setattr('tmdbsimple.Search', FakeTmdbsimpleSearch)
    
    class FakeTmdbsimpleMovies():
        _example_movie_data = {
            1: {
                'title': 'Example Movie',
                'vote_average': 10.0
            }
        }
    
        def __init__(self, tmdb_id):
            self.tmdb_id = tmdb_id
    
        def info(self):
            self.title = self._example_movie_data[self.tmdb_id]['title']
            self.vote_average = self._example_movie_data[self.tmdb_id]['vote_average']
    
    class FakeTmdbsimpleFind():
        def __init__(self, identifier):
            pass
    
        def info(self, external_source):
            return {
                'movie_results': [
                    {'id': 1}
                ]
            }
    
    class FakeTmdbsimpleSearch():
        def __init__(self):
            pass
    
        def movie(self, query):
            return {
                'results': [
                    {
                        'id': 1
                    }
                ]
            }
    
    def test_creation_of_Movie(testMovie):
        assert isinstance(testMovie, movie.Movie)
    
    def test_properties_of_Movie(testMovie):
        assert testMovie.tmdb_id == 1
        assert testMovie.title == 'Example Movie'
        assert testMovie.vote_average == 10
        assert testMovie.watched == False
    

    这具有不需要pytest-mock 的优点(以及工作的优点......),但单独修补MoviesFindSearch 似乎并不优雅。如果我想用tmdbsimple 的假版本测试其他模块,还需要重新实现假类。我试图模拟整个包但不知道如何模拟,这也让我的完成主义者感到恼火。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-15
      • 2016-08-17
      • 1970-01-01
      • 2016-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多