I'm having an issue with the testability of my code. This is related to my class layout and my python package layout.
I hope for one of the following outcomes for this question:
The base class is AuthenticationToken
. The two classes HardwareToken
and Keyfile
inherit from it.
AuthenticationToken
s can be serialized into a string and vice versa. This is how I implement deserialization:
class AuthenticationToken(object):
@classmethod
def try_deserialize(cls, spec: str):
for subclass in cls.__subclasses__():
token = subclass.try_deserialize(spec)
if token:
return token
return None
I have one file per class and put them into a package directory
package
+-- __init__.py
+-- authentication_token.py
+-- hardware_token.py
+-- keyfile.py
Now I prefer to reference a class like package.Keyfile
instead of package.keyfile.Keyfile
. Also all subclass definitions of Authentication token have be seen by python before I can use the try_derialize
method. This is why I import all classes in __init__.py
:
from .authentication_token import AuthenticationToken
from .hardware_token import HardwareToken
from .keyfile import Keyfile
Now I would like to unit test the AuthenticationToken
class without referencing its subclasses. The idea is to write a TestAutheticationToken
class and use it as single subclass during the test:
import unittest
from package import AuthenticationToken
class TestSubclass(AuthenticationToken):
pass
class TestAuthenticationToken(unittest.TestCase):
# This test fails
def test_bad_case(self):
should_be_none = AuthenticationToken.try_deserialize("Keyfile")
self.assertIsNone(should_be_none)
if __name__ == '__main__':
unittest.main()
This test fails because try_deserialize
creates an object of type Keyfile
. This is because __init__.py
is evaluated. This is also the case if I import AuthenticationToken
directly from the module:
from package.authentication_token import AuthenticationToken
So the question is: how can I prevent the classes Keyfile
and HardwareToken
from being imported when testing AuthenticationToken
?
Or otherwise how do I change the class and/or package layout so I can import all classes independtly from each other while still preserving the benefits mentioned above?
The code is hard to test, because the list of token providers is implicit . It depends on which modules have been loaded. I suggest you at least give the option of an explicit list of token providers. Either require the token providers to be registered at start up, or provide an optional parameter for the list of token providers.
Here's the simplest change I can think of:
class AuthenticationToken(object):
@classmethod
def try_deserialize(cls, spec: str, token_providers=None):
if token_providers is None:
token_providers = cls.__subclasses__()
for subclass in token_providers:
token = subclass.try_deserialize(spec)
if token:
return token
return None
Now your regular code is unchanged, and your test can look like this:
def test_bad_case(self):
should_be_none = AuthenticationToken.try_deserialize(
"Keyfile",
token_providers=[TestSubclass])
self.assertIsNone(should_be_none)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.