繁体   English   中英

如何模拟查询 SQLAlchemy 中的连接表和 alchemy-mock where-statement 从多个表中过滤数据时的模拟?

[英]How to mock query for joined tables in SQLAlchemy and alchemy-mock when where-statement filters data from multiple tables?

我有架构:

CREATE SCHEMA problem AUTHORIZATION blockchain;

CREATE TABLE problem.users (
    id serial PRIMARY KEY NOT NULL,
    nick varchar NOT NULL
);

CREATE TABLE problem.passwords_keys (
    id serial PRIMARY KEY NOT NULL,
    user_id serial references problem.users,
    password varchar NOT NULL,
    valid bool NOT null DEFAULT TRUE
);

对于模式,我使用sqlacodegen生成了 SLQAlchemy 类:

export DB_PASSWORD=...
export DB_USER=...
sqlacodegen "postgresql://${DB_USER}:${DB_PASSWORD}@localhost/default_database --schema problem | tee schema.py
# coding: utf-8
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, text
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
metadata = Base.metadata


class User(Base):
    __tablename__ = 'users'
    __table_args__ = {'schema': 'problem'}

    id = Column(Integer, primary_key=True, server_default=text("nextval('problem.users_id_seq'::regclass)"))
    nick = Column(String, nullable=False)


class PasswordsKey(Base):
    __tablename__ = 'passwords_keys'
    __table_args__ = {'schema': 'problem'}

    id = Column(Integer, primary_key=True, server_default=text("nextval('problem.passwords_keys_id_seq'::regclass)"))
    user_id = Column(ForeignKey('problem.users.id'), nullable=False, server_default=text("nextval('problem.passwords_keys_user_id_seq'::regclass)"))
    password = Column(String, nullable=False)
    valid = Column(Boolean, nullable=False, server_default=text("true"))

    user = relationship('User')

我想模拟以执行查询的那些类:

SELECT p."password" "password" FROM 
    problem.passwords_keys p 
    JOIN problem.users u ON(p.user_id=u.id) 
    WHERE p."valid" = true AND u.nick = 'Bush';

所以我创建了代码:

from mock_alchemy.mocking import UnifiedAlchemyMagicMock  # pip3 install mock-alchemy
from unittest import mock
import schema

nick = 'Bush'

session = UnifiedAlchemyMagicMock(data=[
    (
        [
            mock.call.query(schema.PasswordsKey),
            mock.call.join(schema.User),
            mock.call.filter(schema.PasswordsKey.valid == True),
            mock.call.filter(schema.User.nick == nick),
        ],
        [schema.PasswordsKey(id=2, user_id=22, password='qwerty1', valid=True)]
    ),
])

keys_for_user = session.query(schema.PasswordsKey)\
    .join(schema.User)\
    .filter(schema.PasswordsKey == True)\
    .filter(schema.User.nick == nick)
password = keys_for_user.first().password
print(f'1. Found private key {password} by nick "{nick}"')

但它不起作用:

Traceback (most recent call last):
  File "/home/agh/Pulpit/blockchain/watra-ledger/src/stack/mock.py", line 32, in <module>
    password = keys_for_user.first().password
AttributeError: 'NoneType' object has no attribute 'password'

有趣的是,当我不使用另一个表进行过滤时 - 一切正常:

from mock_alchemy.mocking import UnifiedAlchemyMagicMock  # pip3 install mock-alchemy
from unittest import mock
import schema

session = UnifiedAlchemyMagicMock(data=[
    (
        [
            mock.call.query(schema.PasswordsKey),
            mock.call.join(schema.User),
            mock.call.filter(schema.PasswordsKey.valid == True),
         ],
        [schema.PasswordsKey(id=1, user_id=11, password='qwerty', valid=True)]
    ),
])

keys_for_user = session.query(schema.PasswordsKey)\
    .join(schema.User)\
    .filter(schema.PasswordsKey.valid == True)
row = keys_for_user.first()
print(f'2. Found valid password "{row.password}"')

结果是:

2. Found valid password "qwerty"

我发现的另一个解决方案不是模拟过滤,但它对我来说看起来很脏(IMO 代码只是让测试通过而不是测试):

from mock_alchemy.mocking import UnifiedAlchemyMagicMock  # pip3 install mock-alchemy
from unittest import mock
import schema

nick = 'Bush'

session = UnifiedAlchemyMagicMock(data=[
    (
        [
            mock.call.query(schema.PasswordsKey),
            mock.call.join(schema.User),
            # mock.call.filter(schema.PasswordsKey.valid == True),
            # mock.call.filter(schema.User.nick == nick),
         ],
        [schema.PasswordsKey(id=1, user_id=11, password='qwerty', valid=True)]
    ),
])

keys_for_user = session.query(schema.PasswordsKey)\
    .join(schema.User)\
    .filter(schema.PasswordsKey == True)\
    .filter(schema.User.nick == nick)
password = keys_for_user.first().password
print(f'3. Found private key {password} by nick "{nick}"')

不幸的是,我需要按用户昵称进行过滤,并且必须有某种方法可以使它正常工作。

我终于发现 - 查询连接表没有问题 - 即使对于多个过滤器, mock.call.filter也应该被调用一次是个问题。

所以不是:

session = UnifiedAlchemyMagicMock(data=[
    (
        [
            mock.call.query(schema.PasswordsKey),
            mock.call.join(schema.User),
            # mock.call.filter(schema.PasswordsKey.valid == True),
            # mock.call.filter(schema.User.nick == nick),
         ],
        [schema.PasswordsKey(id=1, user_id=11, password='qwerty', valid=True)]
    ),
])

我们应该这样做:

session = UnifiedAlchemyMagicMock(data=[
    (
        [
            mock.call.query(schema.PasswordsKey),
            mock.call.join(schema.User),
            mock.call.filter(schema.PasswordsKey.valid == True, 
                             schema.User.nick == nick),
         ],
        [schema.PasswordsKey(id=1, user_id=11, password='qwerty', valid=True)]
    ),
])

链接到官方文档中的示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM