User Model and the Tdd
So I've got BMI name per specific bmi value. #10 issue.
I've started work on it. Went to master branch. Created feature-branch.
And then.... I've found out django-models that has no test-coverage or other things like that and looks a bit more then little as not TDD-approach.
Well... let's go and fix that, shall we ?
Fixing User Model not tested yet.
So I've figure out I'll first create test for checking if fields exists in model.
Initially I've created test method that was a bit product-specific, but then finally I've moved it to a GenericModelTestCase
class and added test only in this generic-type class.
Then in specific-model-class I only inherited by this generic-type class and added cls
and fields
fields :)
The GenericModelTestCase
looks like that (in generics.py
module):
"""
All generic type of classes used in django-tests
"""
from django.test import TestCase
from django.contrib.auth.models import User as AuthUser
class GenericModelTestCase(TestCase):
"""
Generic Model Test Case.
Contains:
- assert_fields - assertion for checking if all fields in model exists
"""
cls = AuthUser
fields = []
def assert_fields(self, fields):
""" asserting if fields exists in model"""
fields_in_model = []
# fields as django fields:
for field in self.cls._meta.get_fields(include_hidden=True):
if field.name in fields:
fields_in_model.append(field.name)
# method as fields:
for method in self.cls.__dict__:
if method in fields and method not in fields_in_model:
fields_in_model.append(method)
assert sorted(set(fields_in_model).intersection(fields)) == sorted(fields)
def test_fields(self):
""" Uses asserting fields for checking with naming convention of django-type """
self.assert_fields(self.fields)
And the UserModelTestCase
"""
Unit tests for User model
"""
from web.models import User
from web.tests.django_type.generics import GenericModelTestCase
class UserModelTestCase(GenericModelTestCase):
""" User Model TestCase """
cls = User
fields = ['id', 'name', 'surname', 'weight', 'height', 'bmi']
Clean and simple ! :)
Now... how about that feature branch, huh?
Yeah I know I should not mix feature branch with a fixing lack-of-tests branch, but for sake of simplicity I'll skip that this time.
Now moving to the feature it self.
It should be pretty simple, isn't it ?
Let's find it out ! :)
Feature branch code
At models.py
, User class :
class User(models.Model):
"""
User model for obtaining personal information about biking riders
"""
name = models.CharField(max_length=50)
surname = models.CharField(max_length=100, default="")
weight = models.IntegerField(default=0)
height = models.IntegerField(default=0)
def __unicode__(self):
"""
Returns User information when using str/printing
"""
return self.name
def bmi(self):
"""
Body Mass Index calculator simplified to number
"""
return (self.weight / (self.height * self.height)) * 10000
def bmi_health_name(self):
"""
BMI Health Name - Returns proper naming for value of BMI
"""
if self.bmi() < 0:
return None
if self.bmi() >= 0 and self.bmi() <= 18.5:
return "Underweight"
if self.bmi() > 18.5 and self.bmi() <= 24.9:
return "Normal weight"
if self.bmi() > 24.9 and self.bmi() <= 29.9:
return "Overweight"
if self.bmi() > 29.9:
return "Obesity"
At tests_user.py
:
"""
Unit tests for User model
"""
from mock import MagicMock
from web.models import User
from web.tests.django_type.generics import GenericModelTestCase
class UserModelTestCase(GenericModelTestCase):
""" User Model TestCase """
cls = User
fields = ['id', 'name', 'surname', 'weight', 'height', 'bmi']
def setUp(self):
"""SetUp"""
self.user = User()
def assert_bmi_health(self, bmi, expected):
""" Asserts BMI health name"""
self.user.bmi = MagicMock(return_value=bmi)
assert self.user.bmi_health_name() == expected
def test_bmi_health_name(self):
""" tests for bmi_health_name method """
self.assert_bmi_health(-1, None)
self.assert_bmi_health(0, "Underweight")
self.assert_bmi_health(1, "Underweight")
self.assert_bmi_health(18.5, "Underweight")
self.assert_bmi_health(18.6, "Normal weight")
self.assert_bmi_health(25.0, "Overweight")
self.assert_bmi_health(30.0, "Obesity")
Code commits done for this post:
- Refactors code. Removes useless pylint disable
- Refactors code.
- Moves bmi_health_name into User Model
- Fixes test for obesity
- Adds failing test for obesity
- Fixes overweight test case
- Adds failing overweight test
- Fixes normal weight test
- Adds failing test for normal weight
- Refactors code for bmi-health-name
- Adds tests for underweight for bmi-health-name
- Adds passing test for 0 element
- Adds failing test for bmi_health_name
- Fixes pylint issues
- Refactors code. Moves generic class to generics module.
- Refactors code
- Adds fields as methods check for existing
- Adds User Model testing fields
Tools and applications used:
- Vi/VIM
- Docker
- Tmux
- anselmos/dotfiles
- anselmos/linux-utils
- Tomatoid
Release
You can find release of source code here.
Acknowledgements of links I've found usefull while writing this article:
DjangoProjects.com:
StackOverFlow:
- Check if model field exists in Django
- Python: Passing a class name as a parameter to a function?
- How can I compare two lists in python and return matches
- Get model's fields in Django
Accomplished:
TDD Approach for Django Models!
Final word! -> NEW BADGE UNLOCKED!
You have one new badge unlocked ! - Making Django Generic Model Test Case! - Huuuraaay ! :)
Check my 1st Milestone here
Will I be able to complete it till Sunday?
Give a comment in box below :)
Thanks ! Keep in touch :)
Comments
comments powered by Disqus