Reputation: 751
In the following code, I'd like to test that if config_dir
doesn't exist, it is created.
def __init__(
self, config_dir: pathlib.Path = pathlib.Path().home() / ".config" / "moe",
):
"""Read configuration.
Args:
config_dir: Path of the configuration directory.
"""
self.config_dir = config_dir
if not self.config_dir.exists():
self.config_dir.mkdir(parents=True)
self.db_path: pathlib.Path = self.config_dir / "library.db"
# initialize db
engine = sqlalchemy.create_engine("sqlite:///" + str(self.db_path))
library.Session.configure(bind=engine)
library.Base.metadata.create_all(engine) # create tables if they don't exist
However, I can't seem to figure out how to just test that part of the code. I've tried
def test_config_dir_dne(self, mocker):
"""We should create the config directory if it doesn't exist."""
fake_path = mocker.Mock()
moe.config.Config(fake_path)
fake_path.mkdir.assert_called_once_with(parents=True)
But, pathlib doesn't like that I use the specific join operator with the mock.
> self.db_path: pathlib.Path = self.config_dir / "library.db"
E TypeError: unsupported operand type(s) for /: 'Mock' and 'str'
Also, I don't really care about that part of the code for this test, nor do I wish to create the engine at the end.
How can I just test check that mkdirs
is called on my fake_path
without running any of the other code?
Upvotes: 0
Views: 119
Reputation: 532268
Add an additional argument to __init__
, the function used to initialize the database. By default, you'll use a function supplied by the class itself.
def __init__(
self,
config_dir: pathlib.Path = pathlib.Path().home() / ".config" / "moe",
db_initializer=None
):
"""Read configuration.
Args:
config_dir: Path of the configuration directory.
"""
self.config_dir = config_dir
if not self.config_dir.exists():
self.config_dir.mkdir(parents=True)
self.db_path: pathlib.Path = self.config_dir / "library.db"
if db_initializer is None:
db_intializer = self._init_db
db_initializer(self.dp_path)
@staticmethod
def _init_db(p):
engine = sqlalchemy.create_engine("sqlite:///" + str(p))
library.Session.configure(bind=engine)
library.Base.metadata.create_all(engine)
When you test the function, you'll pass a do-nothing function instead.
def test_config_dir_dne(self, mocker):
"""We should create the config directory if it doesn't exist."""
fake_path = mocker.Mock()
moe.config.Config(fake_path, lambda p: None)
fake_path.mkdir.assert_called_once_with(parents=True)
Upvotes: 0
Reputation: 71562
There are three general approaches here:
Mock out more. Specifically, you could patch out pathlib.Path
and sqlalchemy.create_engine
and everything else that you don't want to test.
Use dependency injection so you can mock without patching. Have the constructor take abstract interfaces and then have your test pass in dummy versions of those.
Restructure your code so that each unit you want to test on its own is its own function. Take the logic that you want to test (the creation of the config dir), put that into its own function, and then unit-test that function independently of your constructor.
Upvotes: 2