Flask+ Dependency-injecter+pytest 写测试类

news2024/11/17 22:48:55

最近在使用这几个在做项目,因为第一次用这个,所以不免有些问题。总结下踩的坑

1.测试类位置

首先测试类约定会放在tests里面,不然有可能发生引入包的问题,会报错某些包找不到。

2. 测试类依赖注入

这里我就用的真实的数据库操作,但是我用了一个专门为测试写的事务管理,所有操作都不会commit而是会rollback,相当于一个内存数据库了,只会在内存里面,不会提交,你也可以直接用内存数据库或者mock,

# tests/test_app.py
import pytest
from main.application import create_app
from main.config.database_config import DatabaseConfig
from main.containers import Container
from dependency_injector.wiring import inject, Provide
from main.services.common_service.db_access.domain.user import User
from main.services.common_service.db_access.service.user_service import UserService
from dependency_injector.providers import Factory
from main.services.common_service.db_access.repository.impl.user_repository_impl import UserRepositoryImpl

#获取app
@pytest.fixture
def app():
    app = create_app()
    with app.app_context():
        yield app
#获取user_service
@pytest.fixture
def user_service(app):
    with app.app_context():
        user_repo=UserRepositoryImpl()
        db=app.container.db()
        yield UserService(user_repository=user_repo,session_factory=db.force_rowback_session)
        
#测试方法,需要传入user_serivice,会从上面加了@pytest.fixture注解获取同名方法进行注入
def test_create_user(user_service):
    user=user_service.create_user()
    assert user is not None

我的userservice需要传入两个参数,一个是repo的实现类,一个是sqla的session

"""Containers module."""

import os
from dependency_injector import containers, providers

from main.config.database_config import DatabaseConfig



class Container(containers.DeclarativeContainer):
  
    # wiring_config = containers.WiringConfiguration(auto_wire=True)
    wiring_config = containers.WiringConfiguration(packages=[
        "main.config", 
        "main.web.controller",
        "main"
    ])
    config_path = os.path.join(os.path.dirname(__file__), "../config.yml")
    config = providers.Configuration(yaml_files=[config_path])
    
    db=providers.Singleton(DatabaseConfig,db_url=config.db.url)

有个很重要的一点就是
这里如果写成这样过的话,启动项目是没有什么问题。但是测试类的时候就会加载不到,就会导致需要config的类加载不到你需要的配置。

config = providers.Configuration(yaml_files=['config.yml'])

所以必须写成这样

config_path = os.path.join(os.path.dirname(__file__), "../config.yml")
config = providers.Configuration(yaml_files=[config_path])

事务控制 

"""Database module."""

from contextlib import contextmanager, AbstractContextManager
from typing import Callable

from sqlalchemy import create_engine, orm,event
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from datetime import datetime

from main.services.common_service.db_access.domain.common_field_entity import CommonEntity
Base = declarative_base()

class DatabaseConfig:

    def __init__(self, db_url: str) -> None:
        self._engine = create_engine(db_url, echo=True)
        self._session_factory = orm.scoped_session(
            orm.sessionmaker(
                autocommit=False,
                autoflush=False,
                expire_on_commit=False,
                bind=self._engine,
            ),
        )

    def create_database(self) -> None:
        Base.metadata.create_all(self._engine)


    @contextmanager
    def session(self) -> Callable[..., AbstractContextManager[Session]]:
        session: Session = self._session_factory()
        try:
            yield session
        except Exception:
            session.rollback()
            raise
        else:
            if session._transaction.is_active:
                session.commit()
            session.close()
            
    @contextmanager
    #专门负责测试类的回滚操作,不论任何情况,都进行回滚操作
    def force_rowback_session(self) -> Callable[..., AbstractContextManager[Session]]:
        session: Session = self._session_factory()
        try:
            yield session
        except Exception:
            session.rollback()
            raise
        else:
            session.rollback()
            session.close()

    @event.listens_for(CommonEntity, 'before_insert', propagate=True)
    def before_insert_listener(self, mapper, target):
        # 在创建时自动更新 created_dt,version
        target.created_dt = datetime.now()
        target.created_by = 'MAAS'
        target.version = 1
        
    @event.listens_for(CommonEntity, 'before_update', propagate=True)
    def before_update_listener(self, mapper, target):
        # 在更新时自动更新 updated_dt,version
        target.updated_dt = datetime.now()
        target.updated_by = 'MAAS'
        target.version += 1

运行测试

就直接到文件目录,执行pytest命令就可以了,没有pytest就pip install 一下就行了

pytest xxx.py

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1378927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[AutoSar]BSW_OS 01 Autosar OS入门(一)

目录 关键词平台说明一、Autosar OS 的位置二、Autosar OS 与OSEK三、TASK 关键词 嵌入式、C语言、autosar、OS、BSW 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C,C编译器HighTec (GCC) 一、Autosar OS 的位置 如在[AutoSar]基础部分 a…

如何使用统计鸟网站统计分析网站流量来源?

统计鸟官网地址:https://www.tongjiniao.com/ 站长必备!网站数据统计,流量监测平台 提供网站数据统计分析、搜索关键词、流量访问来源等服务 深入分析用户点击习惯,为智能化运营网站提供更好的用户体验 目录 一、注册账号信息 二…

电位器的基本知识

一、电位器简介 电位器是一种可调的电子元件。它是由一个电阻体和一个转动或滑动系统组成。当电阻体的两个固定触电之间外加一个电压时,通过转动或滑动系统改变触点在电阻体上的位置,在动触点与固定触点之间便可得到一个与动触点位置成一定关系的电压。…

DFT中的SCAN、BIST、ATPG基本概念

DFT中的SCAN、BIST、ATPG基本概念 SCAN 定义 扫描路径法是一种针对时序电路芯片的DFT方案,目标是在不影响正常功能的情况下来能够提高可控性和可观测性。 原理 原理是将时序电路可以模型化为一个组合电路网络和带触发器(Flip-Flop,简称FF)的时序电路…

【开源】基于JAVA的数据可视化的智慧河南大屏

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏,包含了GDP、…

蚁群算法(ACO)解决旅行商(TSP)问题的python实现

TSP问题 旅行商问题(Travelling Salesman Problem, 简记TSP,亦称货郎担问题):设有n个城市和距离矩阵D [dij],其中dij表示城市i到城市j的距离,i, j 1, 2 … n,则问题是要找出遍访每个城市恰好一次的一条回…

c#多线程中使用SemaphoreSlim

SemaphoreSlim是一个用于同步和限制并发访问的类,和它类似的还有Semaphore,只是SemaphoreSlim更加的轻量、高效、好用。今天说说它,以及如何使用,在什么时候去使用,使用它将会带来什么优势。 代码的业务是&#xff1a…

InseRF: 文字驱动的神经3D场景中的生成对象插入

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

UE5 简易MC教程学习心得

https://www.bilibili.com/video/BV12G411J7hV?p13&spm_id_frompageDriver&vd_sourceab35b4ab4f3968642ce6c3f773f85138 ———— 目录 0.摧毁逻辑学习 1.发光材质灯方块 2.封装。想让子类 不更改父类的变量。 3.材质命名习惯。 0.摧毁逻辑学习 达到摧毁的条件…

多国管理中心多语言区块链源码一元夺宝程序仿趣步奕跑/原生计步器/原生人脸识别

前后台分开的,后台是TP3.2的框架了。 目前把整体UI 改版黄色系风格,集成了一元夺宝程序,用户数据同步趣步,效果看起来很棒,另外加入股票走势图(K线图),目前已经继承人脸识别&#xf…

数据结构——二叉树(先序、中序、后序及层次四种遍历(C语言版))超详细~ (✧∇✧) Q_Q

目录 ​​​​​​​ 二叉树的定义: *特殊的二叉树: 二叉树的性质: 二叉树的声明: 二叉树的先序遍历: 二叉树的中序遍历: 二叉树的后序遍历: 二叉树的层序遍历: 二叉树的节…

【工具使用-A2B】32通道24bit传输的配置方法

一,简介 本文记录A2B总线上压缩为24bit的方法和如何计算是否超带宽的方法。 二,具体操作 2.1 计算是否超带宽的方法 确认传输带宽占比 2.2 32通道24bit配置 Downstream Compression选择Disable。 三,总结 本文主要介绍,如…

如何在Spring Boot中使用EhCache缓存

1、EhCache介绍 在查询数据的时候,数据大多来自于数据库,我们会基于SQL语句与数据库交互,数据库一般会基于本地磁盘IO将数据读取到内存,返回给Java服务端,我们再将数据响应给前端,做数据展示。 但是MySQL…

红队专题-Golang工具ChYing

Golang工具ChYing 招募六边形战士队员原chying工具代码分析并发访问控制并发 原子 写入读取 通道嵌套映射结构初始化启动代理服务器重启代理服务器 招募六边形战士队员 一起学习 代码审计、安全开发、web攻防、逆向等。。。 私信联系 原chying工具代码分析 前有 Chying 后有…

什么是云服务器,阿里云优势如何?

阿里云服务器ECS英文全程Elastic Compute Service,云服务器ECS是一种安全可靠、弹性可伸缩的云计算服务,阿里云提供多种云服务器ECS实例规格,如经济型e实例、通用算力型u1、ECS计算型c7、通用型g7、GPU实例等,阿里云百科aliyunbai…

C++|44.智能指针

文章目录 智能指针unique_ptr特点一——无法进行复制 shared_ptr特点一——可复制特点二——计数器(用于确定删除的时机) 其他 智能指针 通常的指针是需要特殊地去申请对应的空间,并在不使用的时候还需要人工去销毁。 而智能指针相对普通的指…

2024 1.6~1.12 周报

一、上周工作 论文研读 二、本周计划 思考毕业论文要用到的方法或者思想,多查多看积累可取之处。学习ppt和上周组会内容、卷积神经网络。 三、完成情况 1. 数据训练的方式 1.1 迁移学习 迁移学习是一种机器学习方法,把任务 A 训练出的模型作为初始模…

Java8常用新特性

目录 简介 1.默认方法 2..Lambda表达式 3.Stream API 4.方法引用 5.Optional类 简介 Java 8是Java编程语言的一个重要版本,引入了许多令人兴奋和强大的新特性。这些特性使得Java程序更加现代化、灵活和高效。让我们一起来探索一些Java 8的常用新特性吧&#…

css3基础语法与盒模型

css3基础语法与盒模型 前言CSS3基础入门css3的书写位置内嵌式外链式导入式(工作中几乎不用)行内式 css3基本语法css3选择器标签选择器id选择器class类名原子类复合选择器伪类元素关系选择器序号选择器属性选择器css3新增伪类![在这里插入图片描述](https…

【Java SE语法篇】6.数组

📚博客主页:爱敲代码的小杨. ✨专栏:《Java SE语法》 ❤️感谢大家点赞👍🏻收藏⭐评论✍🏻,您的三连就是我持续更新的动力❤️ 文章目录 1.数组的基本概念1.1 为什么使用数组?1.…