py3相对import和mock的问题

Posted end-emptiness

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了py3相对import和mock的问题相关的知识,希望对你有一定的参考价值。

〇、前言

  本文用于记录博主用自己的方法编写mock的时候,与py3相对import的机制发生问题的情况

 

一、问题描述

  情境:

    我想测试view_b的代码,但是view_b依赖view_a,为了测试,我要隔离view_a

  代码结构:

    技术图片

  代码:

  view_a.py:

import jiliguala

def viewfunca():
    print("do view a")

  view_b.py:

from . import view_a

def viewfuncb():
    view_a.viewfunca()
    print("do view b")

  test_core2.py:

# -*- coding: utf-8 -*-

import sys
import unittest
import unittest.mock as mock

@mock.patch.dict("sys.modules", {
    "test.view.view_a": mock.Mock(),
})
class TestCore(unittest.TestCase):
    @mock.patch("test.view.view_a")
    def test1(self, oMockViewa):
        def funca():
            print("do mock a")
        from test.view import view_b
        oMockViewa.side_effect = funca
        print()
        view_b.viewfuncb()

    def test2(self):
        print(sys.modules)

  在pycharm中用unittest运行test1之后出现问题

技术图片

File "D:python 3.6.7libunittestmock.py", line 1217, in get_original "%s does not have the attribute %r" % (target, name)
AttributeError: <module ‘test.view‘ from ‘E:game_box estview\\__init__.py‘> does not have the attribute ‘view_a‘

  问题描述:

    我隔离了view_a,为什么告诉我初始化view_a的时候出错了?哪里的初始化?

 

二、原因分析

  如果通过删删减减做过几次实验,会发现,如果把test1的装饰器去了,这个就不报错了(代码要改一下)。

  那么可以确认问题,问题出在技术图片这条语句上。

  运行test2:

  技术图片

  分析:

  1、代码中用于mock的类装饰器的原理是,在系统变量中,加入对应的路径。(从test_core2的test2中可以测试)

  2、mock.patch是对环境中的test内的view内的view_a,进行mock

  概括一下就是:我虽然在系统变量里加了test.view.view.a,但是我没有【加入test,以及在test里面加入view,在view加入view_a】这个操作。

  

  但是仔细看代码

  b.py代码中用了from . import view_a这句话,应该做了之前说的那个初始化操作,为什么没有?

  原因:py3中import的执行策略,如果在系统路径中检测到这个路径,直接返回,不会进行初始化操作

    这是py代码的优化,但是这个优化,坑了这种mock写法……

  验证方法:

    python import源码

    这个是python __import__函数的源码:

    技术图片

    应该很容易理解,所以这种mock写法,会导致父模块没有被初始化

 

三、解决方法

  1、如果是py2环境,不要用相对导入,直接import

  2、在__init__.py中对添加view_a的接口

  3、如果不嫌麻烦,不要隔离view_a,可以隔离更具体的接口,比如view_a中的【jiliguala】这种必须隔离的模块(因为不隔离就会报错)

  4、换种mock写法,在此不提供了

 

四、总结

  用装饰器写mock,而且直接mock掉想隔离的模块很方便,但是找bug很麻烦。

  这里也是感谢某位师兄翻看python源码帮我解决了这个bug(这个不看源码很难找到,因为这种优化不会写文档里面)

  

以上是关于py3相对import和mock的问题的主要内容,如果未能解决你的问题,请参考以下文章

相对导入兼容性问题 py2 和 py3

python中常用的mock介绍

java 如何mock参数

python接口自动化测试 - mock模块基本使用介绍

`from ... import`与`import .` [重复]

Spock框架Mock对象方法经验总结#yyds干货盘点#