Mocking away the url object in Pylons - python

I want to test a method that calls the pylons.url object. However calling this in the tests leads to an error:
TypeError: No object (name: url) has been registered for this thread
So I'd like to replace the pylons.url object with a Mock from the mock library.
#patch('pylons.url')
def my_test(self, url_mock):
...
However, this doesn't seem to replace the url object.
Is there a way to mock out this object?

In order for patch to work you need to give it the full path to the variable in the MODULE that you are actually patching. So rather than 'pylons.url' you would be patching 'my_project.my_module.url' and inside my_module.py you would be doing
from pylons import url
Does that make sense?

Related

pytest mocker.patch.object's return_value uses different mock than the one I passed it

I'm using pytest to patch the os.makedirs method for a test. In a particular test I wanted to add a side effect of an exception.
So I import the os object that I've imported in my script under test, patch it, and then set the side effect in my test:
from infrastructure.scripts.src.server_administrator import os
def mock_makedirs(self, mocker):
mock = MagicMock()
mocker.patch.object(os, "makedirs", return_value=mock)
return mock
def test_if_directory_exist_exception_is_not_raised(self, administrator, mock_makedirs):
mock_makedirs.side_effect = Exception("Directory already exists.")
with pytest.raises(Exception) as exception:
administrator.initialize_server()
assert exception.value == "Directory already exists."
The problem I ran into was that when the mock gets called in my script under test, the side effect no longer existed. While troubleshooting I stopped the tests in the debugger to look at the ID values for the mock I created and the mock that the patch should have set as the return value and found that they are different instances:
I'm still relatively new to some of the testing tools in python, so this may be me missing something in the documentation, but shouldn't the returned mock patched in here be the mock I created?? Am I patching it wrong?
UPDATE
I even adjusted the import style to grab makedirs directly to patch it:
def mock_makedirs(self, mocker):
mock = MagicMock()
mocker.patch("infrastructure.scripts.src.server_administrator.makedirs", return_value=mock)
return mock
And I still run into the same "different mocks" issue.
:facepalm:
I was patching incorrectly. I'm considering just deleting the whole question/answer, but I figured I'd leave it here in case someone runs into the same situation.
I'm defining the patch like this:
mocker.patch.object(os, "makedirs", return_value=mock)
Which would be a valid structure if I was patching the result a function/method. That is, what this patch is saying is "when you call the makedirs, return this.
What I actually want to do is return a mock in place of the method. In it's current form it makes sense that I see two different mocks because the patch logic is currently "replace makedirs with a new mock and then when that mock is called, return this other mock (the mock I made)"
What I really want is just:
mocker.patch.object(os, "makedirs", mock)
Where my third argument (in the patch.object form) is the mock module parameter (vs the named return_value parameter).
In retrospect, it's pretty obvious when I think about it which is why I'm considering deleting the question, but it's an easy enough trip-up that I'm going to leave it live for now.

`requests_mock` applies to all requests even if they are not set and throws NoMockAddress exception

I found that requests_mock used as a fixture with pytest applies to all requests even if they are not set.
I'm not sure if it's a requests_mock/pytest bug or I'm missing something. Eventually, I don't need to mock 'api-b' call but I can't find out how to avoid it.
def test_reqs(requests_mock):
requests_mock.get('https://api-a.com/')
requests.get('https://api-b.com/')
assert requests.get('https://api-a.com/')
I'm writing integration tests using pytest, requests-mock, and pytest-mock for an API endpoint. Under the hood, this endpoint makes several calls to different third party API's that I need to mock.
Some of those calls can be mocked by requests_mock.
But some of them can't because they make a call from the inside of a third-party module.
I tried to use pytest-mock to mock the last one and found out that it basically doesn't work.
requests_mock is still trying to mock that call and throws the next error:
requests_mock.exceptions.NoMockAddress: No mock address: GET https://api-b.com/
As requests-mock doc says, you can achieve this behavior by setting real_http=True while initiating the requests_mock.Mocker().
with requests_mock.Mocker(real_http=True) as m:
m.get('http://test.com', text='resp')
requests.get('http://test.com').text
But nothing said of how to use it with pytest.
As pytest test receives request_mock object as an argument (fixture), you can set it in your test explicitly.
def test_reqs(requests_mock):
requests_mock.real_http = True
requests_mock.get('https://api-a.com/')
requests.get('https://google.com/')
assert requests.get('https://api-a.com/')

Mock pyarrow.parquet using patch

Is possible to mock the declaration import pyarrow.parquet as pq?
I'm trying to mock it as:
#patch("twilio_events.workers.EngineDWH")
#patch("twilio_events.workers.pyarrow")
#patch("twilio_events.workers.s3fs")
def test_process_batch(s3fs, py, e):
pass;
But it throws me an exception Attribute Error....<workspace/myproject/twilio_events/workers.py'> does not have the attribute 'pyarrow'>.
Other mocks are fine, only this one is throwing this exception :(.
Any help?
The target of a patch is the name that needs to be replaced. In this case, if paraquet is imported as pq, you have to replace pq.
Try something like
#patch("twilio_events.workers.pq")
This is explained a bit in the docs:
patch() works by (temporarily) changing the object that a name points to with another one. There can be many names pointing to any individual object, so for patching to work you must ensure that you patch the name used by the system under test.
The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.

Mock method in test file from different directory python

I am attempting to mock a method and running into issues with mock actually overwriting it.
app/tests/test_file.py <- contains the the unit test, currently using:
#mock.patch('app.method', return_value='foo')
def test(self, thing):
...
do some stuff with app/main/server.py
and get its response, assert a few values
...
assert 'foo' is in return value of some stuff
The method being mocked is being called by another file that server.py is calling.
app/main/server.py <- what the unit test is actually interacting with
app/main/route.py <- where method being mocked is called
app/main/thing.py <- contains method to be mocked
This is with python 2.7 and each package has an init file. The parent folder (app) contains imports for every class and method. I've tried app.method which doesn't give problems, but doesnt work. I've tried thing.method, throws an error. I've tried app.main.thing.method which does nothing.
I've had success in this same test suite mocking an object and one of its methods, but that object is created and used directly in the server.py file. I'm wondering if it's because the method being called is so far down the chain. Mocking is pretty magical to me, especially in Python.
After more digging finally figured it out, will leave it up for any others that have problems (especially since it's not easily Google'able).
As #Gang specified the full path should work, however the module needs to be the module where the method is called, not the module where it is located, as this person points out. . Using #Gang example:
#mock.patch('app.main.route.method')
def test(self, mock_method):
mock_method.return_value = 'foo'
# call and assert

Using flexmock on python modules

I've been searching on internet to find an example of using flexmock on python modules, but all doc's seem to be for object/class. I'm wondering if it's possible to mock some variables returned by a module. What if that module calls another module?
ex.
def function_inside_function(id, some_string):
test_log = {"id": id, "definition": some_string}
return test_log
def function1(id):
some_string = 'blah' + id # i am totally bs-ing here
log = function_inside_function(id, some_string)
return log
so now I want to test each function separately by using flexmock to mock some values
back then when doing the same thing with an object, I could do (say the object is assigned to be test_object)
flexmock(test_object).should_receive('some_func').and_return('some_value')
where some_func is being called inside that object
but when I try to do the same with a module, I kept getting
FlexmockError: <function function1 at some_address> does not have attribute function_inside_function
I want to know if it's possible to use flexmock on modules, and, if yes. how?
After a lot of research and trial n error, it turns out that I have to use sys.modules
say my module is imported from path.to.module, then the syntax would be
flexmock(sys.modules['path.to.module']).should_receive('function.of.object').and_return(response)
function.of.object is the function being called. For example, requests.get. using only get will not work.
response is the response you try to mock. in the requests.get example, the response would be a requests.Response(), and then you can use setattr to set the attributes if flexmock complains about it. (Is there a better way to do it?)

Categories

Resources