zoukankan      html  css  js  c++  java
  • 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\test\view\__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(这个不看源码很难找到,因为这种优化不会写文档里面)

      

  • 相关阅读:
    Chapter 17_1 弱引用table
    Chapter 16_5 单一方法
    Chapter 16_4 私密性
    Chapter 16_3 多重继承
    Chapter 16_2 继承
    Chapter 16_1 Class
    Chapter 16_0 面向对象编程
    小米2s刷机
    Chapter 15_4 子模块和包
    ASP.NET Core MVC 泛型接口的声明调用与注入服务
  • 原文地址:https://www.cnblogs.com/end-emptiness/p/12111098.html
Copyright © 2011-2022 走看看