How to add the fictive method - python

Inside of the class, I use VK API. During the test, I use MagicMock to mock authorization in the API this class uses:
vk.Session = MagicMock(name='session', return_value=None)
vk.API = MagicMock(name='api')
mock_vk_values = [{'uid': user.vk_uid}]
vk.API.users.get = MagicMock(name='uid', return_value=mock_vk_values)
But, inside of the class, I use this API to get the user's uid:
class VKAuth(object):
def __init__(self, access_token, user):
...
self.session = vk.Session(access_token=access_token)
self.api = vk.API(self.session)
...
def authenticate(self):
try:
vk_uid = self.api.users.get()[0]['uid']
On this place it gets an error:
*** AttributeError: 'NoneType' object has no attribute 'users'
How to mock this stuff right?
Thank you!

Try this:
vk.Session = MagicMock(name='session', return_value=None)
mock_vk_values = [{'uid': user.vk_uid}]
# create an explicit mock for the users attribute
users_mock = MagicMock(name='users')
users_mock.get = MagicMock(name='uid', return_value=mock_vk_values)
# create a mock for the api
api_mock = MagicMock(name='api', users=users_mock)
# this is where a lot of people get mocking wrong -
# mistaking the mock of a constructor with the object returned/created
vk.API = MagicMock(return_value=api_mock)

Related

How can I intercept database requests(Peewee ORM) in pytest?

I have a main database and a replica (they are the same in the test environment):
core_db = PooledPostgresqlExtDatabase(**DB_COFIG)
replica_db = PooledPostgresqlExtDatabase(**DB_REPLICA_COFIG)
A controllers that executes a query in different databases depending on the model
class BaseController:
def _get_logs(self):
query = self.model.select()
if is_instance(self.model, ModelToReplica)
query = query.bind(replica_db)
return list(query)
class ReplicaExampleController(BaseLogsController):
model = ModelToReplica
def process(self):
return self._get_logs()
class BaseExampleController(BaseLogsController):
model = BaseModel
def process(self):
return self._get_logs()
Controllers are linked to two urls:
/get_core_result/ # Returns the result from BaseExampleController (core_db)
/get_replica_result/ # Returns the result from ReplicaExampleController (replica_db)
I want to check that each of the corners accesses the right base. And I know that the reference to the database object is stored in the request object. How do I get it from the test? I'm using a PyTest. I understand that I probably need to use mock, but I don't understand how.
Unfortunately, this is all I have so far:
class TestSwitchDB:
def test_switch_db_to_replica():
url_core = url_for('core_db_controller')
core_result = self.client.get(url_core)
url_replica = url_for('replica_db_controller')
replica_result = self.client.get(url_replica)
In test_switch_db_to_replica you can mock the db query and patch it, something
with patch(BaseController._get_logs) as mock_get_logs:
mock_get_logs.return_value="your expected return"
As a reference: https://docs.python.org/3/library/unittest.mock-examples.html

How to access a Python object attribute in Robot Framework?

I want to access an object attribute variable in Robot Framework tests and then validate the values of these attributes.
I have the following Python class:
class TestingClass(object):
def __init__(self, client_id=None, redirect_uri=None, state=None):
self.client_id = client_id
self.uri = uri
self.state = state
def url_authorize(self):
self.oauthsession = OAuth2Session(client_id=self.client_id, uri=self.uri, state=self.state)
self.authorize_endpoint_url = self.oauthsession.authorization_url(
url="http://localhost:8080/authorize")[0]
return self.authorize_endpoint_url
def authorize(self):
request = requests.get("http://localhost:8080")
self.status_code = request.status_code
self.content = request.content
self.json = request.json
I want to be able to grab any one of the attributes created in the Authorize (authorize method) and validate it. So I want to do something like this:
* Variables
${CLIENT_ID} to
${URI} http://127.0.0.1:8080/callback
${STATE} random
* Settings
Documentation Tests Suite
Library Utilities.TestingClass ${CLIENT_ID} ${URI}
... ${STATE}
*Keywords
*Test Cases
Authorize Test 1
[Documentation] Test that the user is redirected to the authorize
... endpoint
... when accessing the OAuth Client
Url Authorize
Authorize
Should Be Equal As Integers Utilities.TestingClass.status_code 200
However this gives: Utilities.TestingClass.status_code != 200 which is no surprise to me.
How do I grab this attribute to compare it? Would I need to maybe return all the attributes made from the authroize() method in a list/dictionary/some sort of array and then access by indexing? Or is there a more straight forward way to do this with Robot Framework?
You have the following two choices, both will involve the usage of extended variable syntax. I would prefer the second option.
You can use the Get Library Instance keyword, to get the library instance in the test. The you can access its member variables using the extended variable syntax. Here is an example based on your code, I just replaced the return values with constants.
class TestingClass(object):
def __init__(self, client_id=None, redirect_uri=None, state=None):
self.client_id = client_id
self.uri = redirect_uri
self.state = state
def url_authorize(self):
self.oauthsession = None
self.authorize_endpoint_url = "http://localhost:8080/authorize"
return self.authorize_endpoint_url
def authorize(self):
self.status_code = 200
self.content = 'content'
self.json = { "A" : ["StringA1", "StringA2"], "B": ["StringB1","StringB2"]}
*** Settings ***
Library Utilities.TestingClass to http://127.0.0.1:8080/callback random
*** Test Cases ***
Test
Url Authorize
Authorize
${TestingClass}= Get Library Instance Utilities.TestingClass
Log ${TestingClass.status_code}
Log ${TestingClass.content}
Log ${TestingClass.json}
The other option is to modify the authorize method, so it will return what requests.get("http://localhost:8080") returns. Then you could access the status code, content and JSON in the same way as above, using the extended variable syntax.
class DummyRequestReturnValue():
def __init__(self):
self.status_code = 200
self.content = 'content'
self.json = { "A" : ["StringA1", "StringA2"], "B": ["StringB1","StringB2"]}
class TestingClass(object):
def __init__(self, client_id=None, redirect_uri=None, state=None):
self.client_id = client_id
self.uri = redirect_uri
self.state = state
def url_authorize(self):
self.oauthsession = None
self.authorize_endpoint_url = "http://localhost:8080/authorize"
return self.authorize_endpoint_url
def authorize(self):
request = DummyRequestReturnValue()
return request
*** Settings ***
Library Utilities.TestingClass to http://127.0.0.1:8080/callback random
*** Test Cases ***
Test
Url Authorize
${response}= Authorize
Log ${response.status_code}
Log ${response.content}
Log ${response.json}

Mock instance method of a model class by replacing it with a new method

I want to replace the instance method "set_email_id" with a new function in unit tests by mocking. The instance method returns the email_id field but I want a new function to print the same field. I am not sure which mocking feature to use. I read about side_effect, mock.object etc. I couldn't make it work.
Script:
class Myclass(models.Model):
email_id = models.CharField(max_length=128, null=True, blank=True)
def return_email_id(self):
return self.email_id
def run():
my_class = Myclass()
my_class.email_id = 1
#want to moke this call
my_class.return_email_id()
Unit test:
#patch('models.Myclass.return_email_id')
def test(self, mock_email_id):
def new_method():
# this should replace the instance method
print(self.email_id)
# I know this wouldn't work. I just wanted to show it as an example
mock_email_id.side_effect = new_method()
Refer to this example.
mook_2.py
from datetime import datetime
import requests
from constants import STATUS_ENDPOINT
class BuildStatus:
"""The CI status of a pull request."""
#staticmethod
def build_date() -> str:
return datetime.utcnow().isoformat()
#classmethod
def notify(cls, merge_request_id, status):
build_status = {
"id": merge_request_id,
"status": status,
"built_at": cls.build_date(),
}
response = requests.post(STATUS_ENDPOINT, json=build_status)
response.raise_for_status()
return response
test_mook.py
from unittest import mock
from constants import STATUS_ENDPOINT
from mock_2 import BuildStatus
#mock.patch("mock_2.requests") # mock_2.requests를 호출하면 mock_requests 모의 객체가 대신할것
def test_build_notification_sent(mock_requests):
build_date = "2018-01-01T00:00:01"
with mock.patch("mock_2.BuildStatus.build_date", return_value=build_date): # build_date 호출시 설정한 날짜 반환하도록함,
BuildStatus.notify(123, "OK")
expected_payload = {"id": 123, "status": "OK", "built_at": build_date}
mock_requests.post.assert_called_with(
STATUS_ENDPOINT, json=expected_payload
)
constants.py
"""Definitions"""
STATUS_ENDPOINT = "http://localhost:8080/mrstatus"

Python error 'NoneType' object has no attribute '<....>'

I'm having trouble with the following code:
import tweepy
from tweet import TweetBuilder
from libs.session import Session
class GameHandler:
open_sessions = []
def get_session(self, sessionname):
for session in GameHandler.open_sessions:
#FOLLOWING STATEMENT GOES WRONG
if session.roomname == sessionname:
return session
return None
def session_create(self, sessionname, owner_id, owner_name):
new = Session(sessionname, owner_id, owner_name).add_player(owner_id, owner_name)
GameHandler.open_sessions.append(new)
return TweetBuilder.new_session(sessionname, owner_name)
def session_join(self, sessionname, player_id, player_name):
session = self.get_session(sessionname)
if session != None:
session.add_player(player_id, player_name)
return TweetBuilder.join_session(session, player_name)
return ""
Also part of the Session class:
class Session:
def __init__(self, name, owner_id, owner_name):
#keep track of tweets
self.tweetid_start = None
self.tweetid_current = None
#game elements
self.roomname = name
#THIS LINE WORKS CORRECTLY
print(self.roomname)
self.players = []
self.currentround = None
self.roundnumber = 0
self.players.append(Player(owner_id, owner_name))
When I call session_create() everything works fine. The app runs Session.__init__(), the print statement prints self.roomname.
When I call session_join(), and session_join() calls get_session() problems arise. The for loop is supposed to iterate over the Session-array called open_sessions, but the moment it tries to access the Session-attribute called 'roomname' it gives me the following error:
'NoneType' object has no attribute 'roomname'
Why are my Session-objects suddenly NoneType?
Thanks in advance.
The problem is here:
new = Session(sessionname, owner_id, owner_name).add_player(owner_id, owner_name)
GameHandler.open_sessions.append(new)
By immediately calling add_player on the newly created Session, new is not the Session but the result of add_player, and whatever that does, it seems to return None. Thus you are adding a bunch of None objects to your open_sessions list. Use this instead:
new = Session(sessionname, owner_id, owner_name)
new.add_player(owner_id, owner_name)
GameHandler.open_sessions.append(new)
Or if you want to keep it the way it was, you could change your Session class to provide kind of a "fluent interface" and have add_player (and other methods) return self:
class Session:
...
def add_player(self, id_, name):
....
return self

Python Unit Test Mocking

If you have seen my other question you will know I am having a very hard time at the moment with unit tests in Python. Two days of trying and I've made no progress.
In my method which is part of a Class there is several calls to a DAL.
car_registration = self.dal.cars.get_by_registration('121D121')
This DAL is configured in the base class. I want to completely overrid/mock these calls when running my unit tests and instead return predefined responses so I can continue with the method and ensure everything works as expected.
The method starts of with:
def change_registration(self):
body = json.loads(self.request.body)
registration = body['registration']
car = self.dal.cars.get_by_registration(registration)
My Python test file at the moment is:
class CarTestCase(unittest.TestCase):
def setUp(self):
self.car_controller = CarController()
def test_change_registrations(self):
self.car_controller.dal.cars.get_by_registration = MagicMock(return_value=3)
response = self.car_controller.change_registration()
I am expecting to get the response 3. However, an error is being thrown.
AttributeError: 'CarController' object has no attribute '_py_object'
It appears the mocking isn't working and it still trying to use the main DAL which isn't fully set up when using the unit tests. How do I prevent it for looking for the actual DAL but instead mocks?
I think you are not showing us the code that triggers the error because there is nothing wrong with your strategy. Using some imagination to mimic code we don't have I can write this and it runs with no problem:
import unittest
from unittest.mock import MagicMock
class CarList():
def get_by_registration(self, registration):
pass
class Dal:
def __init__(self):
self.cars = CarList()
pass
class CarController:
def __init__(self):
self.dal = Dal()
def change_registration(self):
registration = None
car = self.dal.cars.get_by_registration(registration)
return car
class CarTestCase(unittest.TestCase):
def setUp(self):
self.car_controller = CarController()
def test_change_registrations(self):
self.car_controller.dal.cars.get_by_registration =\
MagicMock(return_value=3)
result = self.car_controller.change_registration()
self.assertEqual(result, 3)
unittest.main()
Here my example:
# test_tool_file.py
import unittest
from unittest.mock import patch, Mock, call
import test_tools_file
class MyObject():
def __init__(self, data):
self.data = data
def get_data(self):
return self.data
def count(self):
return len(self.get_data())
class TestFile(unittest.TestCase):
""" Cas de tests.
"""
#patch("test_tools_file.MyObject.get_data")
def test_1(self, mock_get):
""" test_1
"""
mock_get.return_value = [1,2,3,4,5,6]
obj = MyObject(["12", "13"])
result = obj.count()
self.assertEqual(result, 6)
Personal opinion: I would suggest you to start simple. Use anything 'magic' once you understand what it provides you over the non-magic way. Using a non-magic solution tends to be easier to understand.
I think you have multiple simple solutions at hand. For what you tried to achieve, instead of:
self.car_controller.dal.cars.get_by_registration = MagicMock(return_value=3)
You could try:
self.car_controller.dal.cars.get_by_registration = lambda: 3
But you mentioned you want to replace all methods. In fact I would consider a 'simple' dependency injection. If you find it difficult to test, it might be a sign that the another design would be better (that's the idea of TDD - Test Driven Design). A simple dependency injection is one where you for example just pass dal to the constructor of CarController.
Here is a complete example with some variations of the test:
from unittest import TestCase
from unittest.mock import Mock
class SomeDal(object):
def get_something(self):
return 'something'
class SomeController(object):
def __init__(self, dal):
self.dal = dal
def upper_something(self):
return self.dal.get_something().upper()
class TestSomeController(TestCase):
def test_using_simple_patch(self):
controller = SomeController(SomeDal())
controller.dal.get_something = lambda: 'anything'
assert controller.upper_something() == 'ANYTHING'
def test_using_simple_patch_and_injection(self):
dal = SomeDal()
dal.get_something = lambda: 'anything'
controller = SomeController(dal)
assert controller.upper_something() == 'ANYTHING'
def test_using_simple_mock_class(self):
class MockDal(object):
def get_something(self):
return 'anything'
controller = SomeController(MockDal())
assert controller.upper_something() == 'ANYTHING'
def test_using_semi_magic_mock(self):
mock_dal = Mock(spec=SomeDal)
mock_dal.get_something.return_value = 'anything'
controller = SomeController(mock_dal)
assert controller.upper_something() == 'ANYTHING'

Categories