Flask+ Dependency-injecter+pytest 写测试类

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

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()
            #rollback之后就不能再close了,否则就会报错
            #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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值