Python 如何使用pytest对sqlalchemy orm类进行单元测试

Python 如何使用pytest对sqlalchemy orm类进行单元测试,python,python-3.x,sqlalchemy,pytest,Python,Python 3.x,Sqlalchemy,Pytest,我想编写一些py.test代码来测试基于创建的2个简单的sqlalchemy ORM类。问题是,如何将py.test中的数据库设置为测试数据库,并在测试完成后回滚所有更改?是否可以模拟数据库并运行测试,而不实际连接到de数据库 以下是我的课程代码: 从sqlalchemy导入创建_引擎,ForeignKey 从sqlalchemy.ext.declarative导入声明性基础 从sqlalchemy导入列,整数,字符串 从sqlalchemy.orm导入sessionmaker,关系 eng=

我想编写一些py.test代码来测试基于创建的2个简单的sqlalchemy ORM类。问题是,如何将py.test中的数据库设置为测试数据库,并在测试完成后回滚所有更改?是否可以模拟数据库并运行测试,而不实际连接到de数据库

以下是我的课程代码:


从sqlalchemy导入创建_引擎,ForeignKey
从sqlalchemy.ext.declarative导入声明性基础
从sqlalchemy导入列,整数,字符串
从sqlalchemy.orm导入sessionmaker,关系
eng=创建引擎('mssql+pymssql://user:pass@主机/我的_数据库')
Base=声明性_Base(英语)
会话=会话生成器(英语)
实习课程=课程()
类作者(基本):
__tablename_u=“作者”
AuthorId=列(整数,主键=True)
名称=列(字符串)
账簿=关系(“账簿”)
def添加_书(自我,标题):
b=书籍(标题=标题,AuthorId=self.AuthorId)
实习课程。添加(b)
intern_session.commit()
教材(基本):
__tablename_u=“图书”
BookId=列(整数,主键=True)
标题=列(字符串)
AuthorId=列(整数,ForeignKey(“Authors.AuthorId”))
作者=关系(“作者”)

我通常这样做:

  • 我不使用模型声明实例化引擎和会话,而是只声明一个没有绑定的基:

    Base=declarative_Base()
    
    我只在需要时使用创建会话

    engine=创建引擎(“”)
    db_session=sessionmaker(bind=engine)
    
    您也可以不使用
    add\u book
    方法中的
    intern\u session
    ,而是使用
    session
    参数

    def添加书(自我、会话、标题):
    b=书籍(标题=标题,AuthorId=self.AuthorId)
    会议.添加(b)
    session.commit()
    
    它使您的代码更易于测试,因为您现在可以在调用该方法时通过您选择的会话。 而且,您不再需要绑定到硬编码数据库url的会话

  • 使用其属性将自定义的
    --dburl
    选项添加到pytest

    只需将其添加到顶级的
    conftest.py

    def pytest\u addoption(解析器):
    parser.addoption('--dburl',
    行动='商店',
    默认值=“”,
    help='要用于测试的数据库的url')
    
    现在可以运行
    pytest--dburl

  • 然后可以从fixture中检索
    dburl
    选项

    • 从自定义装置:

      @pytest.fixture()
      def db_url(请求):
      return request.config.getoption(“--dburl”)
      # ...
      
    • 在测试中:

      def测试(请求):
      db_url=request.config.getoption(“--dburl”)
      # ...
      

  • 此时,您可以:

    • 在任何测试或夹具中获取测试
      db_url
    • 使用它来创建引擎
    • 创建绑定到引擎的会话
    • 将会话传递给已测试的方法
    在每个测试中都这样做是非常混乱的,因此您可以有效地使用pytest装置来简化过程

    以下是我使用的一些固定装置:

    从sqlalchemy导入创建引擎
    从sqlalchemy.orm导入作用域的_会话,sessionmaker
    @pytest.fixture(scope='session')
    def db_发动机(请求):
    “”“生成一个SQLAlchemy引擎,该引擎在测试会话后被抑制”“”
    db_url=request.config.getoption(“--dburl”)
    引擎=创建引擎(db\U url,echo=True)
    屈服发动机_
    引擎处理()
    @pytest.fixture(scope='session')
    def db_会话_工厂(db_发动机):
    “”“返回SQLAlchemy作用域的会话工厂”“”
    返回作用域_会话(sessionmaker(bind=db_引擎))
    @pytest.fixture(scope='function')
    def db_会话(db_会话工厂):
    “”“生成测试后回滚的SQLAlchemy连接”“”
    session=db\u session\u工厂()
    收益期_
    会话.回滚()
    会话\结束()
    
    使用
    db_会话
    fixture,您可以为每个测试获得一个干净的
    db_会话

    测试结束时,回滚
    db\u会话
    ,保持数据库干净。

    Nice!它成功了,有没有办法使用pytest的fixture来模拟数据库?因此,测试不需要与数据库建立真正的连接,我可以测试所有方法是否工作正常。@Feulo使用pytest和库可能会使用假数据库模拟,但这是我从未做过的。我将查看unnitest.mock文档。谢谢使用此选项后,它似乎无法在测试期间处理session.commit调用,因为没有嵌套事务。这意味着测试可能会提交到数据库。我可能错了,因为我没有完全掌握一切。但这对我很有用:@Tryph如果您有一个从Base继承的中间接口呢?为了扩展SQLAlchemy ORM的一些功能。非常感谢!