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
fields fields :)
GenericModelTestCase looks like that (in
""" 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)
""" 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
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"
""" 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:
You can find release of source code here.
Acknowledgements of links I've found usefull while writing this article:
- 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
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 :)