Mocking Subproces Calls in Python - python

I am trying to use mock.patch decorator in the mock library but am having problems. I have this init function where I make two subprocess calls. The first time in self.setPackageQuota() and the second time in self.setBandwidthLimit().
class User():
def __init__(self, username, plan, domain, owner, diskQuota, diskUsed):
self.setUsername(username)
self.setPlan(plan)
self.setDomain(domain)
self.setOwner(owner)
self.setDiskQuota(diskQuota)
self.setDiskUsed(diskUsed)
self.setPackageQuota()
self.setBandwidthLimit()
self.setPackageQuota()
def setBandwidthLimit(self):
whmapicall = subprocess.Popen(["whmapi1" , "showbw", 'searchtype=user', 'search=^%s$' % self.username], stdout=subprocess.PIPE)
whmapireturn = whmapicall.stdout.read().split("\n")
for line in whmapireturn:
self.setPackageQuota()
def setPackageQuota(self):
whmapicall = subprocess.Popen(["whmapi1" , "getpkginfo", "pkg=%s" % self.plan], stdout=subprocess.PIPE)
whmapireturn = whmapicall.stdout.read().split("\n")
for line in whmapireturn:
I would much rather patch whmapireturn to be something else. I also would not like to not run subprocess.Popen. My inital thought was to patch out #mock.patch('subprocess.Popen', MockedClass) and #mock.patch('whmapireturn', OtherMockedClass) but I cant seem to get it to work. How would I test an init fucntion like this while patching out things that I can't have in my enviroment? Thanks in advance for any asistance.

I comprehend from your question that you want to mock-patch the Subprocess.Popen() call from your file. For that you should use the following approach:-
Let take the scenario where your file name is for which you want to create the unittest. Therefore:- in the unittest file, you should write:-
import library
#mock.patch("library.subprocess")
def test_subprocess_call(mock_subprocess):
mock_subprocess.Popen.return_value = None
I hope you find your answer.

Related

How should I test a method of a mocked object

I have a question about how to mock a nested method and test what it was called with. I'm having a hard time getting my head around: https://docs.python.org/3/library/unittest.mock-examples.html#mocking-chained-calls.
I'd like to test that the "put" method from the fabric library is called by the deploy_file method in this class, and maybe what values are given to it. This is the module that gathers some information from AWS and provides a method to take action on the data.
import json
import os
from aws.secrets_manager import get_secret
from fabric import Connection
class Deploy:
def __init__(self):
self.secrets = None
self.set_secrets()
def set_secrets(self):
secrets = get_secret()
self.secrets = json.loads(secrets)
def deploy_file(self, source_file):
with Connection(host=os.environ.get('SSH_USERNAME'), user=os.environ.get("SSH_USERNAME")) as conn:
destination_path = self.secrets["app_path"] + '/' + os.path.basename(source_file)
conn.put(source_file, destination_path)
"get_secret" is a method in another module that uses the boto3 library to get the info from AWS.
These are the tests I'm working on:
from unittest.mock import patch
from fabric import Connection
from jobs.deploy import Deploy
def test_set_secrets_dict_from_expected_json_string():
with patch('jobs.deploy.get_secret') as m_get_secret:
m_get_secret.return_value = '{"app_path": "/var/www/html"}'
deployment = Deploy()
assert deployment.secrets['app_path'] == "/var/www/html"
def test_copy_app_file_calls_fabric_put():
with patch('jobs.deploy.get_secret') as m_get_secret:
m_get_secret.return_value = '{"app_path": "/var/www/html"}'
deployment = Deploy()
with patch('jobs.deploy.Connection', spec=Connection) as m_conn:
local_file_path = "/tmp/foo"
deployment.deploy_file(local_file_path)
m_conn.put.assert_called_once()
where the second test results in "AssertionError: Expected 'put' to have been called once. Called 0 times."
the first test mocks the "get_secret" function just fine to test that the constructor for "Deploy" sets "Deploy.secrets" from the fake AWS data.
In the second test, get_secrets is mocked just as before, and I mock "Connection" from the fabric library. If I don't mock Connection, I get an error related to the "host" parameter when the Connection object is created.
I think that when "conn.put" is called its creating a whole new Mock object and I'm not testing that object when the unittest runs. I'm just not sure how to define the test to actually test the call to put.
I'm also a novice at understanding what to test (and how) and what not to test as well as how to use mock and such. I'm fully bought in on the idea though. It's been very helpful to find bugs and regressions as I work on projects.

mocking python libraries or wrapper method

In my code release_bundler.py file
class MavenDependenciesPathsBuilderStrategy(PathsBuilderStrategy):
def build_bundler_copy_paths(self, mapping_values):
source = join(getcwd(),'target','dependency',mapping_values[0])
destination = join(getcwd(),'target','generated-resources',mapping_values[1],mapping_values[0])
return [source, destination]
class NestedModulePathsFilterStrategy(FilterStrategy):
def filter_changeset_paths(self, changeset_paths, bundling_map, paths_builder_strategy):
for mapping_key in bundling_map.keys():
if(mapping_key in changeset_paths):
mapping_values = bundling_map.get(mapping_key).values()
copy_paths = paths_builder_strategy.build_bundler_copy_paths(mapping_values)
return copy_paths
If I want to test the filter_changeset_paths method, I'll have to mock both getcwd method inside the build_bundler_copy_paths method or mocking only the latter will do?
I tried mocking the method in my tests release_bundler_test.py, importing classed like this:
from release_bundler import NestedModulePathsFilterStrategy, MavenDependenciesPathsBuilderStrategy
then patch the MavenDependenciesPathsBuilderStrategy class
def mock_build_bundler_copy_paths(self, mapping_values):
return ['/cwd/foo','/cwd/bar']
#mock.patch('release_bundler.MavenDependenciesPathsBuilderStrategy', 'build_bundler_copy_paths', mock_build_bundler_copy_paths)
def test_nested_module_filter_changeset_paths(self):
pc_maps = {'ProcessingComponents/ProcessContainer':['ProcessContainerInstaller-bin.zip','SERVICES/Installer'],'ProcessingComponents/DataGrid':['ProcessContainerInstaller-bin.zip','SERVICES/Installer']}
changed_paths = ['ProcessingComponents/ProcessContainer/ProcessContainerRuntime/main/java/com/suntecgroup/tbms/container/ContainerException.java']
filter_test = NestedModulePathsFilterStrategy()
result = filter_test.filter_changeset_paths(changed_paths,pc_maps, MavenDependenciesPathsBuilderStrategy())
self.assertIsNotNone(result)
self.assertEquals(result[0], '/cwd/foo')
self.assertEquals(result[0], '/cwd/bar')
but I don't think this mock works because self.assertIsNotNone(result) fails
So the questions are:
Am I mocking the right way? can't get my head over it
will mocking on the MavenDependenciesPathsBuilderStrategy method do or I have to mock the os.getcwd inside it's method also?
My bad, my newbie python brains took a while to get a hang of things:
this is how it works well
#mock.patch('release_bundler.MavenDependenciesPathsBuilderStrategy.build_bundler_copy_paths')
def test_filters_pc_from_changeset_paths(self, mock_build_bundler_copy_paths):
pc_maps = {'ProcessingComponents/ProcessContainer':['ProcessContainerInstaller-bin.zip','SERVICES/Installer']}
changed_paths = ['ProcessingComponents/ProcessContainer/ProcessContainerRuntime/main/java/com/suntecgroup/tbms/container/ContainerException.java']
with mock.patch('release_bundler.NestedModulePathsFilterStrategy.do_copy') as mock_do_copy:
mock_do_copy.return_value = ''
filter_test = NestedModulePathsFilterStrategy()
filter_test.filter_changeset_paths(changed_paths,pc_maps, MavenDependenciesPathsBuilderStrategy())
mock_build_bundler_copy_paths.assert_called_with(['ProcessContainerInstaller-bin.zip','SERVICES/Installer'])
mock_do_copy.assert_called()
I might be wrong but I think we cant apply multiple decorators to a method?

Set Spark Config property for spark-testing-base

While I was trying to use spark-testing-base in Python, I needed to test a function which writes on a Postgres DB.
To do so is necessary to provide to the Spark Session the Driver to connect to Posgtres; to achieve that I first tried to override the getConf() method (as reported in the comment Override this to specify any custom configuration.). But apparently it doesn't work. Probably I'm not passing the value with the required syntax or whatever but after many attempts I anyway get the error java.lang.ClassNotFoundException: org.postgresql.Driver (typical of when the Driver Jar was not correctly downloaded through the conf parameter).
Attempted getConf override:
def getConf(self):
return ("spark.jars.packages", "org.postgresql:postgresql:42.1.1")
def getConf(self):
return {"spark.jars.packages", "org.postgresql:postgresql:42.1.1"}
def getConf(self):
return SparkConf()\
.setMaster("local[*]")\
.setAppName("test")\
.set("spark.jars.packages", "org.postgresql:postgresql:42.1.1")
So I even tried to Override the setUp() method like that:
def setUp(self):
try:
from pyspark.sql import Session
self.session = Session.Builder.config("spark.jars.packages", "org.postgresql:postgresql:42.1.1")
self.sqlCtx = self.session._wrapped
except Exception:
self.sqlCtx = SQLContext(self.sc)
But still no luck. So what I am doing wrong? How am I supposed to override the getConf() method?
Not exactly sure how to do this in python. In scala, using sbt, it is quite straight forward. But anyways, the System.setProperty("spark.jars.packages", "org.postgresql:postgresql:42.1.1") method found here: https://github.com/holdenk/spark-testing-base/issues/187 worked for me.
So I would rec looking up how to do that with python + spark.
It was necessary to override the setUpClass method:
#classmethod
def setUpClass(cls):
"""Setup a basic Spark context for testing"""
class_name = cls.__name__
conf = SparkConf().set("spark.jars.packages", "org.postgresql:postgresql:42.1.1")
cls.sc = SparkContext(cls.getMaster(), appName=class_name, conf=conf)
quiet_py4j()
And in this way is then possible to pass to the Spark test library external jars.
Credits to Leonardo Noleto: https://github.com/holdenk/spark-testing-base/issues/281#event-2200108290

python: How to mock helper method?

Can you please help me out to figure what I did wrong? I have the following unit test for a python lambdas
class Tests(unittest.TestCase):
def setUp(self):
//some setup
#mock.patch('functions.tested_class.requests.get')
#mock.patch('functions.helper_class.get_auth_token')
def test_tested_class(self, mock_auth, mock_get):
mock_get.side_effect = [self.mock_response]
mock_auth.return_value = "some id token"
response = get_xml(self.event, None)
self.assertEqual(response['statusCode'], 200)
The problem is that when I run this code, I get the following error for get_auth_token:
Invalid URL '': No schema supplied. Perhaps you meant http://?
I debugged it, and it doesn't look like I patched it correctly. The Authorization helper file is in the same folder "functions" as the tested class.
EDIT:
In the tested_class I was importing get_auth_token like this:
from functions import helper_class
from functions.helper_class import get_auth_token
...
def get_xml(event, context):
...
response_token = get_auth_token()
After changing to this, it started to work fine
import functions.helper_class
...
def get_xml(event, context):
...
response_token = functions.helper_class.get_auth_token()
I still don't fully understand why though
In your first scenario
in tested_class.py, get_auth_token is imported
from functions.helper_class import get_auth_token
The patch should be exactly the get_auth_token at tested_class
#mock.patch('functions.tested_class.get_auth_token')
Second scenario
With the following usage
response_token = functions.helper_class.get_auth_token()
The only way to patch is this
#mock.patch('functions.helper_class.get_auth_token')
alternative
With import like this in tested_class
from functions import helper_class
helper_class.get_auth_token()
patch could be like this:
#mock.patch('functions.tested_class.helper_class.get_auth_token')
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.
Python documentation has a very good example. where to patch

Using Magic mock to test Github Api

I am basically using magic mock and context manager to test my code, I was successfully able to mock my get_urls function, But I am having trouble mocking out my access_all_repos_pr(): function which contains data of PR newer than 7 days, can anyone help me out on how to mock that data.
Here is the test code for my get_urls():
import unittest
from mock import MagicMock, patch
from contextlib2 import ExitStack
from GithubAPIpackage.GithubAPI import get_urls
class Test_GithubApi(unittest.TestCase):
def test_get_urls_returns_valid_urls(self):
with ExitStack() as stack:
mock_get_urls = stack.enter_context(
patch("GithubAPIpackage.GithubAPI._fetch_url")
)
fake_data = {"current_user_repositories_url": "http://FAKEURL.com"}
mock_get_urls.return_value = fake_data
print(type(fake_data))
result = get_urls()
self.assertEqual(result, "http://FAKEURL.com")
I want to mock out the response for the function access_all_repo_pr, can anyone help me out in what I need to do exactly to create a mock for my access_all_repo_pr function. Do I need to refactor my code in some way? (relatively new to python)
what I am trying is:
class Test_GithubApi_newer_than_7_days(unittest.TestCase):
def test_access_all_repo_pr_returns_valid_response(self):
with ExitStack() as stack:
mock_access_all_repo_pr = stack.enter_context(
patch("GithubAPIpackage.GithubAPI._fetch_url")
)
fake_data = {"current_user_repositories_url": "http://myfakeurl.com"}
mock_access_all_repo_pr.return_value = fake_data
result = access_all_repo_pr()
self.assertEqual(result, "")
Since you are using requests under the hood, may I suggest using responses for your testing? Not trying to skirt the question, but in my experience, I have found this to be the path of least resistance when it comes to writing tests that deal with the requests module. The tests end up being a lot cleaner, safer, and easier to write.

Categories