Reputation: 682
I have a very simple model like this:
class Observation(models.Model):
thing = models.ForeignKey(Thing, on_delete=models.PROTECT)
user_locked = models.BooleanField(default=False)
admin_locked = models.BooleanField(default=False)
created = models.DateTimeField(auto_now=False, auto_now_add=True)
created_by = models.ForeignKey(
User, on_delete=models.PROTECT, related_name="%(class)s_created_by"
)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
updated_by = models.ForeignKey(
User, on_delete=models.PROTECT, related_name="%(class)s_updated_by"
)
def is_fully_locked(self):
"""Return boolean if Observation is locked by both"""
if self.user_locked and self.admin_locked:
return True
return False
I'm trying to test the is_fully_locked()
method and I was curious about creating the Observation
model.
I have used things like Mixer and factory_boy in the past to create objects to test against, but as those projects grew so did the time it took for the tests to run which I'd like to avoid. I have recently started reading about using Mock (I'm still confused by it a little).
My question is - for a simple model like this - what is the best/fastest way to test that method?
Currently I have some test like this:
class ObservationTest(TestCase):
def test_is_fully_locked_with_no_employee_locked(self):
start = time.time()
observation = Observation(admin_locked=True)
print(time.time() - start)
# Takes 5.91278076171875e-05
# FASTEST
self.assertFalse(observation.is_fully_locked())
def test_is_fully_locked_with_no_employee_locked_mixer(self):
start = time.time()
observation = mixer.blend(Observation, admin_locked=True)
print(time.time() - start)
# Takes 0.011276006698608398
# SLOWEST
self.assertFalse(observation.is_fully_locked())
def test_is_fully_locked_with_no_employee_locked_mock(self):
start = time.time()
observation = mock.Mock(spec=Observation)
observation.admin_locked = True
print(time.time() - start)
# Takes 0.0005071163177490234
# SECOND FASTEST - but errors out
self.assertFalse(observation.is_fully_locked())
# This assertion fails: AssertionError: <Mock name='mock.is_fully_locked()' id='4545690352'> is not false
I am also curious - are these each valid ways to test this? I know the mixer method works - but it touches the database, which I believe you're supposed to avoid (and it's probably why it's the slowest).
Questions are as follows:
Is the fastest method above acceptable for testing?
Of the three tests - which would be the 'best practice' - or are all of them acceptable?
With the mock test - how would I write that test to pass (i.e. what did I do wrong)?
Upvotes: 0
Views: 477
Reputation: 670
I'd say all of these approaches are acceptable (however, you probably don't want to mock the object you are testing like you do in test_is_fully_locked_with_no_employee_locked_mock - rather create Observation
object and mock admin_locked
field).
I'm not sure if you are supposed to avoid "touching" the database - Django test runner creates a test database for each run. It is a bit slower, but in some cases that's the only way to test your code. If you can get away with in-memory object (like test_is_fully_locked_with_no_employee_locked test), it can speed up your test.
Mock library is usually used to mock functions that, for example, make a request to external server, run heavy database query, non-deterministic and so on - that are hard to reproduce in test environment.
Upvotes: 2