#MO101 - Test-Driven Development and unit testing in Python with git flavour.
Binary numbers are rather easy to understand, even if becoming familiar with them requires some time.
The package we are going to write will provide a class that represents binary numbers (
Binary) and a class that represents binary numbers with a given bit size (
SizeBinary). They shall provide basic binary operations like logical (and, or, xor), arithmetic (addition, subtraction, multiplication, division), shifts and indexing.
A quick example of what the package shall do:
>>> b = Binary('0101110001') >>> hex(b) '0x171' >>> int(b) 369 >>> b '1' >>> b '0' >>> b.SHR() '10111000'
python -m unittest test_name
Binary system is just a representation of numbers with base 2, just like hexadecimal (base 16) and decimal (base 10). Python can already natively deal with different bases, even if internally numbers are always stored as decimal integers. Let us check it
>>> a = 5 >>> a 5 >>> a = 0x5 >>> a 5 >>> a = 0b101 >>> a 5 >>> hex(0b101) '0x5' >>> bin(5) '0b101'
As you can see Python understands some common bases out of the box, using the
0x prefix for hexadecimal numbers and the
0b for binary ones (and
0o) for octals). However the number is always printed in its base-10 form (
5 in this case). This means however that a binary number cannot be indexed, since integers does not provide support for this operation
>>> 0b101 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not subscriptable
You can also use a different base when converting things to integers, through the
>>> a = int('101', base=2) >>> a 5 >>> a = int('10', base=5) >>> a 5
Simple tasks are the best way to try and use new development methodologies, so this is a good occasion to start working with the so-called test-driven approach. Test-driven Development (TDD) basically means that the first thing you do when developing is to write some tests, that is programs that use what you are going to develop. The purpose of those programs is to test that your final product complies with a given behaviour. So they provide
- Documentation for your API: they are examples of use of your package.
- Regression checks: when you change the code to develop new features they shall not break the behaviour of the previous package versions.
- TODO list: until all tests run successfully you have something still waiting to be implemented.
So now we will pretend we have already developed our Binary class and write some tests that check its behaviour. You will find the whole file in the Resources section at the end of the post, I will show here just some snippets.
import unittest #define Test class as a derived class of the unittest.TestCase base class class Test(unittest.TestCase): def setUp(self): pass def test_init(self): pass
You can use the
-m unittest option when interpreting scripts when extending the base class
unittest.TestCase. Eitherwise you can always add a
main block and call inside all the test methods/functions.
You may find the documentation for unit testing here.
import unittest from binary import Binary #define Test class as a derived class of the unittest.TestCase base class class Test(unittest.TestCase): def setUp(self): self.binary = Binary(6) def test_binary_init_int(self): binary = Binary(6) assert int(binary) == 6
python3 -m unittest test_random
These instructions are present on the gitLab site also after creating a new project.
git config --global user.name "Paun Vladimir Alexandru" git config --global user.email "email@example.com"
mkdir test_proj cd test_proj git init touch README.md git add README.md git commit -m "first commit" git remote add origin firstname.lastname@example.org:paun/test_proj.git git push -u origin master
cd existing_git_repo git remote add origin email@example.com:paun/test_proj.git git push -u origin master
For this project you will work in teams of two. Most of the code will be written together, as a group. One person will write the production code but both decide the content.
One person from the team will be responsible with the production of the test, following the TDD approach. Test are writen before the production code, write just enaugh code to validate the last added code.
- [R1] - writing the tests
- [R2] - writing production code to validate the tests
For this assignment you will work in teams of two on separate computers moat of the time. Every time a team member pushes results on the git repository, the other member will retreive the modifications, add just enaugh code to validate the added test(s) and pushes the results.
Create the test class and a first test method (R1). Add/commit the source to the git and push when it's ready. Get the updates and write the code needed to validate the new test cases (R2).
The ENSTA gitLab address. Please follow the intructions provided on the website.
Chose a name and create a new gitLab project.
First of all we import the class from the
binary.py file (which doesn't exists yet). The
test_binary_init_int() function shall (as the name suggests) initialize a
Binary with an integer. The assertion checks that the newly created binary variable has a consistent integer representation, which is the number we used to initialize it.
We want to be able to initialize a Binary with a wide range of values: bit strings (
'110'), binary strings (
'0b110'), hexadecimal strings (
'0x6'), hexadecimal values (
0x6), lists of integers (
[1,1,0]) and list of strings (
['1','1','0']). The following tests check all those cases, please add them progresively in order to practice the commit/push/pull cycles.
Let us now check that our
Binary class cannot be initialized with a negative number. So for simple binaries we just discard negative numbers.
We want to check that my binary numbers can be correctly converted to integers (through
int()), binary strings (through
bin()), hexadecimals (through
hex()) and to strings (through
str()). We want the string representation to be a plain sequence of zeros and ones, that is the binary string representation without the
Now it is time to add new features to our Binary class. As already said, the TDD methodology wants us to first write the tests, then to write the code. Our class is missing some basic arithmetic and binary operations so the tests are
test_binary_addition_int() test_binary_addition_binary() test_binary_division_int() test_binary_division_rem_int() test_binary_get_bit() test_binary_not() test_binary_and() test_binary_shl_pos()
Have a look here for the syntax used in python to override operators.
We want Binary to support slicing, just like lists. The difference between lists and my Binary type is that for the latter indexes start from the rightmost element. So when we get bits 3:7 of an 8-bit Binary we obtain the four leftmost ones. The desired behaviour is thus exemplified by
>>> b = Binary('01101010') >>> b[4:7] <binary.Binary object at 0x...> (110) >>> b[1:3] <binary.Binary object at 0x...> (101)
The last feature we want to add is a split() function that divides the binary number in two binaries. The rightmost one shall have the given size in bits, while the leftmost just contains the remaining bits.