насмешливая снежинка - PullRequest
0 голосов
/ 28 апреля 2018

У меня есть класс SnowflakeApi на python, который работает как оболочка над классом SnowflakeConnection. Моя Снежинка Апи

import logging
import os
from snowflake.connector import connect    

class SnowflakeApi(object):
    """
    Wrapper to handle snowflake connection
    """

    def __init__(self, account, warehouse, database, user, pwd):
        """
        Handles snowflake connection. Connection must be closed once it is no longer needed
        :param account:
        :param warehouse:
        :param database:
        """
        self.__acct = self._account_url(account)
        self.__wh = warehouse
        self.__db = database
        self.__connection = None
        self.__user = user
        self.__pwd = pwd

    def __create_connection(self):

        try:
            # set the proxy here
            conn = connect(
                account=self.__acct
                , user=self.__user
                , password=self.__pwd
                , warehouse=self.__wh
                , database=self.__db
            )
            return conn
        except:
            raise Exception(
                "Unable to connect to snowflake for user: '{0}', warehouse: '{1}', database: '{2}'".format(
                    self.__user, self.__wh, self.__db))


    def get_connection(self):
        """
        Gets a snowflake connection. If the connection has already been initialised it is returned
        otherwise a new connection is created
        :param credentials_func: method to get database credentials.
        :return:
        """
        try:
            if self.__connection is None:
                self.__connection = self.__create_connection()
            return self.__connection
        except:
            raise Exception("Unable to initalise Snowflake connection")

    def close_connection(self):
        """
        Closes snowflake connection.
        :return:
        """
        self.__connection.close()

Пространство имен для SnowflakeApi - это connection.snowflake_connection.SnowflakeApi (т. Е. У меня есть снежинка_connection.py в папке с именем connection)

Я хочу написать модульные тесты для этого класса, используя pytest и unittest.mock. Проблема в том, что я хочу смоделировать «connect», чтобы объект MagicMock возвращался, а вызов базы данных не производился. До сих пор я пробовал:

  1. monkeypatch.setattr (connections.snowflake_connection, "connect", return_value = "")
  2. Изменил мой исходный класс, чтобы просто импортировать снежинку. Затем я создал фиктивный объект и использовал monkeypatch.setattr (snowke_connection, «снежинка», my_mock_snowflake). Это тоже не сработало

Короче говоря, я пробовал пару других вещей, но ничего не помогло. Все, что я хочу сделать, это смоделировать соединение в виде снежинки, чтобы не было фактического вызова базы данных.

Ответы [ 3 ]

0 голосов
/ 30 апреля 2018

Пример использования unittest.mock и исправления соединения:

from unittest import TestCase
from unittest.mock import patch
from connection.snowflake_connection import SnowflakeApi

class TestSnowFlakeApi(TestCase):

    @patch('connection.snowflake_connection.connect')
    def test_get_connection(self, mock_connect)
        api = SnowflakeApi('the_account', 
                           'the_warehouse', 
                           'the_database', 
                           'the_user', 
                           'the_pwd')

        api.get_connection()

        mock_connect.assert_called_once_with(account='account_url',  # Will be the output of self._account_url()
                                             user='the_user',
                                             password='the_pwd',
                                             warehouse='the_warehouse',
                                             database='the_database')

Если вы тестируете другие классы, которые используют оболочку SnowFlakeApi, то вам следует использовать тот же подход, но в этих тестах исправьте сам SnowFlakeApi.

from package.module.SomeClassThatUsesSnowFlakeApi

class TestSomeClassThatUsesSnowFlakeApi(TestCase):

    @patch('package.module.SnowFlakeApi')
    def test_some_func(self, mock_api):
        instance = SomeClassThatUsesSnowFlakeApi()
        instance.do_something()

        mock_api.assert_called_once_with(...)
        mock_api.return_value.get_connection.assert_called_once_with()

Также обратите внимание, что если вы используете Python 2, вам нужно будет pip install mock, а затем from mock import patch.

0 голосов
/ 07 января 2019

Вот еще один способ, где мы высмеиваем snowflake connector, cursor and fetch_all, используя python mock and patch.

import mock
import unittest
from datetime import datetime, timedelta

import feed_daily_report


class TestFeedDailyReport(unittest.TestCase):
    @mock.patch('snowflake.connector.connect')
    def test_compare_partner(self, mock_snowflake_connector):
        tod = datetime.now()
        delta = timedelta(days=8)
        date_8_days_ago = tod - delta
        query_result = [('partner_1', date_8_days_ago)]
        mock_con = mock_snowflake_connector.return_value
        mock_cur = mock_con.cursor.return_value
        mock_cur.fetchall.return_value = query_result
        result = feed_daily_report.main()
        assert result == True
0 голосов
/ 30 апреля 2018

Использование заглушки и внедрение зависимостей

from ... import SnowflakeApi

def some_func(*args, api=None, **kwargs):
    api = api or SnowflakeApi(...)
    conn = api.get_connection()
    # Do some work
    return result

Ваш тест

class SnowflakeApiStub(SnowflakeApi)
    def __init__(self):
        # bypass super constructor
        self.__connection = MagicMock()      

def test_some_func():
    stub = SnowflakeApiStub()
    mock_connection = stub.__connection
    mock_cursor = mock_connection.cursor.return_value
    expect = ...
    actual = some_func(api=stub)
    assert expect == actual
    assert mock_cursor.execute.called
...