It looks like garbage collection is a problem with python3 -m unittest discover
.
Look at this example:
file: model.py # a basic SQLAlchemy declarative model, as well as the DB manager to insert the data
from sqlalchemy.engine import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Table, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Model(Base):
__tablename__ = 'model'
id = Column('id', Integer, primary_key=True)
name = Column('name', String, unique=True)
val = Column('val', Integer)
class DBManager:
def __init__(self, dbfile):
self.engine = create_engine(f'sqlite:///{dbfile}')
Base.metadata.create_all(bind=self.engine)
def insert_into_db(self, data):
Session = sessionmaker(bind=self.engine)
session = Session()
for model in data:
session.add(model)
session.commit()
session.close()
file: creator.py # it creates a single model and put it into a sqlite database
from model import Model
class ModelCreator:
data = []
def add_data(self, dic):
self.data.append(Model(**dic))
def get_data(self):
if self.data:
return self.data
else:
raise RuntimeError('No data found')
Now the tests (2 separate unittest.TestCase
files with one test each)
file: test_model.py
import unittest
from creator import ModelCreator
class TestModelCreator(unittest.TestCase):
def setUp(self):
self.mc = ModelCreator()
def test_add_data(self):
with self.assertRaises(RuntimeError):
self.mc.get_data()
d = {'name': 'model_test', 'val': 1}
self.mc.add_data(d)
self.assertEqual(len(self.mc.get_data()), 1)
def tearDown(self):
return super().tearDown()
file: test_creator.py
import unittest
import os
from model import DBManager
from creator import ModelCreator
class TestDB(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.dbfile = './test.db'
def setUp(self):
self.dbm = DBManager(self.dbfile)
self.mc = ModelCreator()
def test_insert_into_db(self):
d = {'name': 'model_test', 'val': 1}
self.mc.add_data(d)
to_insert = self.mc.get_data()
self.dbm.insert_into_db(to_insert)
def tearDown(self):
return super().tearDown()
@classmethod
def tearDownClass(cls):
os.remove(cls.dbfile)
Each test runs smoothly when launched separately.
$ python3 -m unittest test_model.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
$ python3 -m unittest test_creator.py
.
----------------------------------------------------------------------
Ran 1 test in 0.014s
OK
BUT
$ python3 -m unittest discover .
.F
======================================================================
FAIL: test_add_data (test_model.TestModelCreator)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/marco/sw/temp/sqlalchemytest/test_model.py", line 12, in test_add_data
self.mc.get_data()
AssertionError: RuntimeError not raised
----------------------------------------------------------------------
Ran 2 tests in 0.013s
FAILED (failures=1)
$ nosetests -vw .
test_insert_into_db (test_creator.TestDB) ... ok
test_add_data (test_model.TestModelCreator) ... FAIL
======================================================================
FAIL: test_add_data (test_model.TestModelCreator)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/marco/sw/temp/sqlalchemytest/test_model.py", line 12, in test_add_data
self.mc.get_data()
AssertionError: RuntimeError not raised
----------------------------------------------------------------------
Ran 2 tests in 0.128s
FAILED (failures=1)
After a couple of print
statements across the tests I found that maybe it is a problem of garbage collection.
So, my question is: how to enforce this?
Looks like the tearDown
methods do not work properly....
It looked solved in Python 3.4. Also, same problem was discusses (with no luck) here back in the days.
Now I'm using Python 3.9.2
Looking forward for any comments. Thanks