Node.js Unit Tests - Use Mocha and Chai to create a Roman Number Library Pt.4
In Pt.3 we implemented the conversion from Hindu-Arabic numbers to Roman numbers. In this final chapter, we will complete our library by adding the conversion from Roman numbers to Hindu-Arabic numbers.
Convert the first five Roman numbers
We will set up the test cases the way we did for the other conversions: we will check both the method toInt() and the method toString(). We will test the first five Roman numbers: ‘I’. ‘II’. ‘III’. ‘IV’ and ‘V’:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
it('The Roman number "I" should be equal to the arabic number 1', () => {
let romannum = RomanNumber('I');
romannum.toString().should.equal('I');
romannum.toInt().should.equal(1);
});
it('The Roman number "II" should be equal to the arabic number 2', () => {
let romannum = RomanNumber('II');
romannum.toString().should.equal('II');
romannum.toInt().should.equal(2);
});
it('The Roman number "III" should be equal to the arabic number 3', () => {
let romannum = RomanNumber('III');
romannum.toString().should.equal('III');
romannum.toInt().should.equal(3);
});
it('The Roman number "IV" should be equal to the arabic number 4', () => {
let romannum = RomanNumber('IV');
romannum.toString().should.equal('IV');
romannum.toInt().should.equal(4);
});
it('The Roman number "V" should be equal to the arabic number 5', () => {
let romannum = RomanNumber('V');
romannum.toString().should.equal('V');
romannum.toInt().should.equal(5);
});
To pass these very first conversion tests, we just need to update the library constructor in this way:
In the code block relative to checkOnlyRomanSymbols we used a Map structure, where we matched Roman numbers (as keys) to Hindu-Arabic numbers (as values).
All the tests passed correctly as shown below:
Convert Roman numbers composed of simple patterns
The next tests we are going to perform, are about ‘simple’ Roman numbers: for simple, we mean those numbers represented by the following patterns:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Patterns:
*
* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
* Ones: | I | II | III | IV | V | VI | VII | VIII | IX |
*
* | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 |
* Tens: | X | XX | XXX | XL | L | LX | LXX | LXXX | XC |
// this loop is used to read the entire Roman string
while(i < val.length) {
let tmpPattern = '';
// this loop is used to build the next pattern
while(i < val.length) {
if(tmpPattern.length == 0) {
tmpPattern += val[i++];
}
else {
let tmpPatternVal = patterns.get(tmpPattern + val[i]);
if(typeof(tmpPatternVal) !== 'undefined') {
tmpPattern += val[i++];
}
else {
break;
}
}
}
if(tmpPattern.length > 0) {
let tmpNum = patterns.get(tmpPattern);
finalInt += tmpNum;
}
}
return finalInt;
};
The first while loop represents a way to read all the string’s symbols, and the second one will be used to look for a single pattern. Any time a pattern is found, we’ll add the relative integer value to the finalInt.
Let’s finally check the tests: $ npm test
…and here we go!
Make our library more resilient
Now that our Roman numbers library is able to correctly convert any valid Roman number, we just want to make it better by adding the last checks for invalid cases:
No more that three equal consecutive symbols can be found;
A pattern relative to a certain set (e.g. hundreds) cannot be found after a higher or equal set (e.g. thousands or hundreds): for example the invalid Roman number “MIM” contains the pattern “M”, belonging to thousands, after “I”, belonging to ones.
Here are the very last test cases to be added inside Check exceptions:
1
2
3
4
5
6
7
it('The constructor invoked with "IIII" should return an exception: invalid value', () => {