This is my sample code:
from path.lib import DBInterface
class MyClass:
def __init__(self):
self.something = "something"
def _my_method(self, some_key, new_setup):
with DBInterface(self.something) as ic:
current_setup = ic.get(some_key)
if current_setup != new_setup:
with DBInterface(self.something) as ic:
ic.set(new_setup)
def public_method(self, some_key, new_setup):
return self._my_method(some_key, new_setup)
(my actual code is bit more complex, but i cant put it here on public :)
Now, what I want to do is, I want to completely mock the imported class DBInterface, because I do not want my unittests to do anything in DB.
BUT I also need the ic.get(some_key) to return some value, or to be more precise, I need to set the value it returns, because thats the point of my unittests, to test if the method behave properly according to value returned from DB.
This is how far I got:
class TestMyClass:
def test_extractor_register(self, mocker):
fake_db = mocker.patch.object('my_path.my_lib.DBInterface')
fake_db.get.return_value = None
# spy_obj = mocker.spy(MyClass, "_my_method")
test_class = MyClass()
# Test new registration in _extractor_register
result = test_class.public_method(Tconf.test_key, Tconf.test_key_setup)
fake_db.assert_has_calls([call().__enter__().get(Tconf.test_key),
call().__enter__().set(Tconf.test_key, Tconf.test_key_setup)])
# spy_obj.assert_called_with(ANY, Tconf.test_key, Tconf.test_key_setup)
assert result.result_status.status_code == Tconf.status_ok.status_code
assert result.result_data == MyMethodResult.new_reg
But i am unable to set return value for call().__enter__().get(Tconf.test_key).
I have been trying many approaches:
fake_db.get.return_value = None
fake_db.__enter__().get.return_value = None
fake_db.__enter__.get = Mock(return_value=None)
mocker.patch.object(MyClass.DBInterface, "get").return_value = None
None of that is actually working and I am running out of options I can think about.
Without having more code or errors that are being produced, it's tough to provide a conclusive answer.
However, if you truly only need to specify a return value for set() I would recommend using MagicMock by virtue of patch --
from unittest.mock import patch
#patch("<MyClassFile>.DBInterface", autospec=True)
def test_extractor_register(mock_db):
mock_db.set.return_value = "some key"
# Rest of test code
Related
I have a class and within that class there are used methods from another classes. Let's say it looks like that (just an example):
from jobs.fruit import Fruit
from jobs.veggie import Veggie
class Healthy:
def start(self, arg):
self.start_connection()
if len(arg) == 1 or self.check(arg):
fruit = Fruit()
fruit.start()
return 1
else:
veggie = Veggie()
veggie.update()
return 0
I'm writting unit tests for my Healthy class, and the start looks like this:
def test_start():
healthy = Healthy()
healthy.start_connection = MagicMock(return_value=1)
healthy.check = MagicMock(return_value=True)
fruit = Fruit()
fruit.start = MagicMock(return_value = 0)
result = healthy.start()
Unfortunately when running the test it doesn't take fruit.start mocked value, but it tries to run original method. How should I mock that class or method and pass that info to the method that I'm testing? I never had to mock something from outside the class that I'm testing and got a bit stuck with it. I'd prefer to stick to unittest mock to not mess with several methods in the code so it's clear for my coworkers. Thanks in advance!
I have a BaseClass and two classes (Volume and testing) which inherits from the BaseClass. The class "Volume" use a method "driving_style" from another python module. I am trying to write another method "test_Score" which wants to access variables computed in the method "driving_style" which I want to use to compute further. These results will be accessed to the class "testing" as shown.
from training import Accuracy
import ComputeData
import model
class BaseClass(object):
def __init__(self, connections):
self.Type = 'Stock'
self.A = connections.A
self.log = self.B.log
def getIDs(self, assets):
ids = pandas.Series(assets.ids, index=assets.B)
return ids
class Volume(BaseClass):
def __init__(self, connections):
BaseClass.__init__(self, connections)
self.daystrade = 30
self.high_low = True
def learning(self, data, rootClass):
params.daystrade = self.daystrade
params.high_low = self.high_low
style = Accuracy.driving_style()
return self.Object(data.universe, style)
class testing(BaseClass):
def __init__(self, connections):
BaseClass.__init__(self, connections)
def learning(self, data, rootClass):
test_score = Accuracy.test_score()
return self.Object(data.universe, test_score)
def driving_style(date, modelDays, params):
daystrade = params.daystrade
high_low = params.high_low
DriveDays = model.DateRange(date, params.daystrade)
StopBy = ComputeData.instability(DriveDays)
if high_low:
style = ma.average(StopBy)
else:
style = ma.mean(StopBy)
return style
def test_score(date, modelDays, params):
"want to access the following from the method driving_style:"
DriveDays =
StopBy =
return test_score ("which i compute using values DriveDays and StopBy and use test_score in the method learning inside
the 'class - testing' which inherits some params from the BaseClass")
You can't use locals from a call to a function that was made elsewhere and has already returned.
A bad solution is to store them as globals that you can read from later (but that get replaced on every new call). A better solution might to return the relevant info to the caller along with the existing return values (return style, DriveDays, StopBy) and somehow get it to where it needs to go. If necessary, you could wrap the function into a class and store the computed values as attributes on an instance of the class, while keeping the return type the same.
But the best solution is probably to refactor, so the stuff you want is computed by dedicated methods that you can call directly from test_score and driving_style independently, without duplicating code or creating complicated state dependencies.
In short, basically any time you think you need to access locals from another function, you're almost certainly experiencing an XY problem.
I have two methods which take different number of arguments. Here are the two functions:
def jumpMX(self,IAS,list):
pass
def addMX(self,IAS):
pass
I am using a function which will return one of these functions to main.I have stored this returned function in a variable named operation.
Since the number of parameters are different for both,how do I identify which function has been returned?
if(operation == jumpMX):
operation(IAS,list)
elif(operation == addMX):
operation(IAS)
What is the syntax for this?Thanks in advance!
You can identify a function through its __name__ attribute:
def foo():
pass
print(foo.__name__)
>>> foo
...or in your case:
operation.__name__ #will return either "jumpMX" or "addMX" depending on what function is stored in operation
Here's a demo you can modify to your needs:
import random #used only for demo purposes
def jumpMX(self,IAS,list):
pass
def addMX(self,IAS):
pass
def FunctionThatWillReturnOneOrTheOtherOfTheTwoFunctionsAbove():
# This will randomly return either jumpMX()
# or addMX to simulate different scenarios
funcs = [jumpMX, addMX]
randomFunc = random.choice(funcs)
return randomFunc
operation = FunctionThatWillReturnOneOrTheOtherOfTheTwoFunctionsAbove()
name = operation.__name__
if(name == "jumpMX"):
operation(IAS,list)
elif(name == "addMX"):
operation(IAS)
You can import those functions and test for equality like with most objects in python.
classes.py
class MyClass:
#staticmethod
def jump(self, ias, _list):
pass
#staticmethod
def add(self, ias):
pass
main.py
from classes import MyClass
myclass_instance = MyClass()
operation = get_op() # your function that returns MyClass.jump or MyClass.add
if operation == MyClass.jump:
operation(myclass_instance, ias, _list)
elif operation == MyClass.add:
operation(myclass_instance, ias)
However, I must emphasize that I don't know what you're trying to accomplish and this seems like a terribly contrived way of doing something like this.
Also, your python code examples are not properly formatted. See the PEP-8 which proposes a standard style-guide for python.
I am new to learn pytest. In bellow sample code.
how can i get A() object in test_one function when fixture is in autouse mode?
import pytest
import time
class A:
def __init__(self):
self.abc = 12
#pytest.fixture(autouse=True)
def test_foo():
print('connecting')
yield A()
print('disconnect')
def test_one():
#how can i get A() object?
print([locals()])
assert 1 == 1
You can always add the fixture as parameter despite the autouse:
def test_one(test_foo):
print(test_foo)
assert 1 == 1
If you don't want to use the fixture parameter for some reason, you have to save the object elsewhere to be able to access it from your test :
a = None
#pytest.fixture(autouse=True)
def test_foo():
global a
a = A()
yield
a = None
def test_one():
print(a)
assert 1 == 1
This could be made a little better if using a test class and put a in a class variable to avoid the use of the global var, but the first variant is still the preferred one, as it localizes the definition of the object.
Apart from that, there is no real point in yielding an object you don't have access to. You may consider if autouse is the right option for your use case. Autouse is often used for stateless setup / teardown.
If your use case is to do some setup/teardown regardless (as suggested by the connect/disconnect comments), and give optional access to an object, this is ok, of course.
This example might not be very good but the original code is way more confusing so I try a simplify version
so I have two class
class Sample():
def __init__(self, sample, antibody = None):
self.sample = sample
self.antibody = None
def pass_in_sample(self, antibody):
if antibody:
self.antibody= antibody
class Antibody():
def__init__(self, sequence):
self.Antibody_sequence= sequence
antibody= Antibody(sequence)
sample_object = A(sample, antibody = antibody)
Now I am parsing a large csv file and some sample exist but there is no antibody
and I am trying to do the following
def main()
for sample in sample_list:
sample_object = Sample(sample)
print(sample_object.Antibody.Antibody_sequence)
but if it antibody is None to begin with, so even if sample_object
was able to create, sample_object.Antibody will return None, so sample_object.Antibody.Antibody_sequence cannot return a name attribute because None type does not have attribute of Antibody_sequence
I know I can filter out None in the main method, but is there anyway to define in the class that if sample_object.Antibodyis None, then their return attribute will also be None, and not give an error
Thank you
Your class Sample has an attribute antibody not Antibody, so I am going to assume that sample_object.Antibody.Antibody_sequence was a typo and that you meant sample_object.antibody.Antibody_sequence as I try to answer your question.
To state specifically (as you did in the comment), when you have the situation where you have:
sample_object.antibody == None
You want:
sample_object.antibody.Antibody_sequence
not to cause an error (throw an exception) but instead result in a None.
This is not possible. Let me illustrate why. The line sample_object.antibody.Antibody_sequence can be written equivalently as:
((sample_object).antibody).Antibody_sequence
So (sample_object) is a reference to an instance of the class Sample. You then reference into that class and see if it has the attribute antibody. If it does it return the value of the attribute. So if that attribute value is None, then what you have is this:
(None).Antibody_sequence
Of course None does not have any attributes, so this fails. The important point here is that no mater what you do inside Sample, you cannot affect what is the None you returned does after it has been returned. So, no there is no way to satisfy your request that 'sample_object.Antibody = None but when I am calling sample_object.Antibody.Antibody_sequence it wouldn't error out but instead just say None'
However, that does not mean that you cannot achieve what you want to do. You just need to use something instead of None as the null antibody initialiser. This below should achieve what you are trying to do.
You can create a class specifically to use instead of the None initialiser for antibody in your Sample class.
class AntibodyNone():
def __bool__(self):
return False
#property
def Antibody_sequence(self):
return None
a_none = AntibodyNone()
class Sample():
def __init__(self, sample, antibody = None):
self.sample = sample
if antibody:
self.antibody = antibody
else:
self.antibody = a_none
def pass_in_sample(self, antibody):
if antibody:
self.antibody = antibody
else:
self.antibody = a_none
s = Sample("sample_val")
s.antibody.Antibody_sequence
In this example s.antibody is uninitialised, but you can still reference s.antibody.Antibody_sequence through it and get None instead of an error.
You can't testsample_object.antibody == None. The __bool__() gives the class 'Falsiness'. So if you test if s.antibody it will be False. That is how you add 'Truthiness' to a class in python 3, if you want to do it for python 2, you need to add __nonzero__().