I am trying to test the following method:
def my_method(self, request, context):
context.set_details('Already exists')
context.set_code(grpc.StatusCode.ALREADY_EXISTS)
To test it, I must pass in a request and a context (which is a grpc.ServicerContext object), like so:
import grcp
def test_my_method(self):
request = {"something": "something-else"}
context = grpc.ServicerContext()
my_method(request, context)
# Assert something here
The problem is, I get the following error when I run my tests:
TypeError: Can't instantiate abstract class ServicerContext with abstract methods add_callback, cancel, invocation_metadata, is_active, peer, send_initial_metadata, set_code, set_details, set_trailing_metadata, time_remaining
How can I get a grpc.ServicerContext object? If I can't, how do I test the method?
grpc.ServicerContext is an abstract class defined with the abc module. In your test you need to write your own concrete subclass of it and pass an instance of that to the method you are testing.
Related
Background
The Django LiveServerTestCase class has a live_server_url method with a #classproperty decorator. (django.utils.functional.classproperty.) The class starts its test server before any tests run, and thus knows the test server's URL before any tests run.
I have a similar MyTestCase class that has a live_server_url #property. It starts a new test server before each test, and thus its live_server_url property can't be a #classproperty because it doesn't know its port until it is instantiated.
To make the API for both consistent, so that all the test utility functions etc. in the code base can be used with both classes, the tests could be written to never reference live_server_url in setUpClass(), before all the tests run. But this would slow down many tests.
Instead, I want MyTestCase.live_server_url to raise a helpful error if it is referenced from the class object rather than an instance object.
Since MyTestCase.live_server_url is a #property, MyTestCase().live_server_url returns a string, but MyTestCase.live_server_url returns a property object. This causes cryptic errors like "TypeError: unsupported operand type(s) for +: 'property' and 'str'".
Actual question
If I could define a #classproperty and #property on the same class, then I would define a MyTestCase.live_server_url #classproperty that raises an error with a helpful message like "live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class".
But when you define 2 methods with the same name in a Python class then the earlier one gets discarded, so I can't do that.
How can I make the behavior of MyTestCase.live_server_url different depending on whether it is called on the class or on an instance?
One option might be to use a metaclass. (Example)
But since I read that metaclasses should usually be avoided, here is what I did:
from django.utils.functional import classproperty
class MyTestCase(TransactionTestCase):
def __getattribute__(self, attr: str):
if attr == 'live_server_url':
return "http://%s:%s" % (self.host, self._port)
return super().__getattribute__(attr)
#classproperty
def live_server_url(self):
raise ValueError('live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class')
MyTestCase().live_server_url calls __getattribute__().
MyTestCase.live_server_url calls the #classproperty definition of live_server_url.
Just subclass property and implement __get__ to raise on error if it is not accessed through an instance :
class OnlyInstanceProperty(property):
def __get__(self, obj, objtype=None):
if obj is None:
raise ValueError('live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class')
return super().__get__(obj, objtype)
Perhaps you can come up with a better name for it. But in any case, you can then use it like so:
class MyTestClass:
def __init__(self):
self.host = "foo"
self._port = "bar"
#OnlyInstanceProperty
def live_server_url(self):
return "http://%s:%s" % (self.host, self._port)
Then:
print(MyTestClass.live_server_url) # http://foo:bar
print(MyTestClass.live_server_url) # ValueError: live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class
And it will work like any other property, so you can use live_server_url.setter and live_server_url.deleter
If you are unfamiliar with __get__, read this HOWTO and it should tell you all you need to know about descriptors, which are an intermediate/advanced Python technique. The descriptor protocol is behind a lot of seemingly magical behavior, and that HOWTO shows you how various things like classmethod, staticmethod, property could be implemented in pure python using the descriptor protocol.
Note, you don't have to inherit from property, a simple bespoke (albeit tightly coupled) approach could be something like:
class LiveServerUrl:
def __get__(self, obj, objtype=None):
if obj is None:
raise ValueError('live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class')
return "http://%s:%s" % (obj.host, obj._port)
class MyTestClass:
live_server_url = LiveServerUrl()
def __init__(self):
self.host = "foo"
self._port = "bar"
I have a class I would like to add typehints to that looks as following:
import yaml
class TestClass(dict):
#classmethod
def load(cls, fname) -> "TestClass":
return cls(yaml.safe_load(""))
#property
#abc.abstractmethod
def test(self):
raise
when I run mypy on a module containing only this class I get the following error message:
error: Cannot instantiate abstract class 'TestClass' with abstract attribute 'test'
From what I have understood from other posts this has something to do with when the 'test' method is instantiated when executing the load method. Is there a way to fix this issue using typehints alone or would I need to adjust my code here?
What mypy is telling you is that TestClass.load(...) will fail, because it will try to create an instance of the abstract class TestClass.
We can fix this by requiring that cls can be called with whatever yaml.safe_load returns (I'm assuming dict here), and returns an instance of TestClass:
from typing import Callable
import abc
import yaml
class TestClass(dict):
#classmethod
def load(cls: Callable[[dict], TestClass], fname) -> "TestClass":
return cls(yaml.safe_load(""))
#property
#abc.abstractmethod
def test(self):
raise
Note that TestClass.load("foo") will now also pass type checking. This is fair to some extent, because it's also fine at runtime, until you call test() on the created instance. I think this might be a limitation of how mypy implements protocols.
I have a class that I want to patch in my unittests.
class OriginalClass():
def method_a():
# do something
def method_b():
# do another thing
Now I created another class to patch it with, so the code for patching it is like
class MockClass(OriginalClass):
def method_a():
# This will override the original method and return custom response for testing.
patcher = patch('OriginalClass', new=MockClass)
mock_instance = patcher.start()
This works exactly as I want it to and I can return whatever responses required for my unittests.
Now this issue is when I want to verify that a method is called with the right parameters in the unittests.
I tried
mock_instance.method_a.assert_called_once()
But it fail with error AttributeError: 'function' object has no attribute 'assert_called_once'.
How can I test the method calls here?
AttributeError: 'function' object has no attribute 'assert_called_once'.
Once mock object is created, there is no method_a exists, you have to call once m.method_a() before assert.
m = mock.create_autospec(OriginalClass)
m.method_a()
m.method_a.assert_called_once()
patch mock entire class
I took it as mock the whole class and all its methods, I would take an example from here
https://docs.python.org/3.3/library/unittest.mock-examples.html
Applying the same patch to every test method, Here is my example, patch the entire Primary class as MockPrimay for every methods and every tests, setup or SetupClass could be added for the methods needed, even the whole class is mocked, but not every methods to be used in the tests.
from tests.lib.primary_secondary import Secondary
#mock.patch('tests.lib.primary_secondary.Primary')
class TestSecondaryMockPrimary(unittest.TestCase):
def test_method_d(self, MockPrimary):
MockPrimary().process()
MockPrimary().process.return_value = 1
oc = Secondary()
self.assertEqual(oc.method_d(), 1)
import tests
self.assertIs(tests.lib.primary_secondary.Primary, MockPrimary)
The Primary is needed for the Secondary for this test
class Primary(object):
def __init__(self, param):
self._param = param
def process(self):
if self._param == 1:
self._do_intermediate_process()
self._do_process()
class Secondary(object):
def __init__(self):
self.scl = Primary(1)
def method_d(self):
return self.scl.process
I think wraps can be useful here:
from unittest.mock import patch
class Person:
name = "Bob"
def age(self):
return 35
class Double(Person):
def age(self):
return 5
with patch('__main__.Person', wraps=Double()) as mock:
print(mock.name) # mocks data
print(mock.age()) # runs real methods, but still spies their calls
mock.age.assert_not_called()
Output:
<MagicMock name='Person.name' id='139815250247536'>
5
...
raise AssertionError(msg)
AssertionError: Expected 'age' to not have been called. Called 1 times.
Calls: [call()].
I'm able to authenticate against some RESTful API with one of the following methods:
password
certificate
anonymous
I've created AuthenticationBase class and its subclassess:
PasswordAuthentication
CertificateAuthentication
AnonymousAuthentication
Basically authentication take two steps:
get_rsts_token
set_access_token - You need RSTS token first
set_access_token has the same implementation across subclasses but get_rsts_token differs so I've tried something like this:
import abc
ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()})
class AuthenticationBase(ABC):
#abc.abstractmethod
def get_rsts_token(self):
pass
def set_access_token()
rsts_token = self.get_rsts_token()
access_token = retrieve_token(rsts_token)
but it leads to an error:
TypeError: Can't instantiate abstract class AuthenticationBase with abstract methods get_rsts_token
I wish to avoid rewriting set_access_token in all subclasses. How to achieve this?
I have the following code:
class Messenger(object):
def __init__(self):
# Class Type of what messages will be created as.
message_class = Message
def publish(self, body):
# Instantiate object of type stored in `message_class`
message = message_class(body)
message.publish()
I want to assert that the Message.publish() method is called. How do I achieve this?
I've already tried the following ways:
Assign message_class to Mock or Mock(). If I debug what message_class(body) returns, it is a Mock, but I don't seem to be able to get the instance and assert it (because the Mock I assign in my test is not the instance used, it is the Type).
Patch Message class with decorator. Whenever I do this it seems like it does not catch it. When I debug what message_class(body) returns its of Message type, not Mock.
Try to mock the __init__ method of message_class in hopes that I can set the instance that is returned whenever the code tries to Instantiate the message. Does not work, throws errors because the __init__ method is not suppose to have a return value.
If you were storing the actual instance, I'd say you could do something like messenger.message.publish.assert_called_once, but since message_class is being stored, it makes it slightly trickier. Given that, you can pull the return_value from the mocked class and check the call that way. Here's how I did it:
Messenger. Note the slight modification to assign message_class to self. I'm assuming you meant to do that, otherwise it wouldn't work without some global funkiness:
'''messenger.py'''
class Message(object):
def __init__(self, body):
self.body = body
def publish(self):
print('message published: {}'.format(self.body))
class Messenger(object):
def __init__(self):
# Class Type of what messages will be created as.
self.message_class = Message
def publish(self, body):
# Instantiate object of type stored in `message_class`
message = self.message_class(body)
message.publish()
Test:
'''test_messenger.py'''
from unittest import mock, TestCase
from messenger import Messenger
class TestMessenger(TestCase):
#mock.patch('messenger.Message')
def test_publish(self, mock_message):
messenger = Messenger()
messenger.publish('test body')
# .return_value gives the mock instance, from there you can make your assertions
mock_message.return_value.publish.assert_called_once()