Test Driven Development For Beginners
I recently volunteered to give a talk on testing techniques a couple of weeks ago, and so I decided to put down my thoughts on some of the different testing techniques we have, why we need to test and real life implications of not testing.
Origin of Test-Driven Development
TDD was developed by Kent Beck in the late 1990’s as part of extreme programming. We at Andela had the opportunity to host Kent last year and it was an unforgettable experience.
One important term Kent always talks about is the Red/Green/Refactor cycle of TDD. Most times, we attach a mantra to it: red means fail and green means pass.
Before now, I used to think that it was not necessary to write the test for something that doesn’t exist. Actually, I test to pass the code. But when I started going deep into testing and looking for reasons why I needed to test, I realised that writing tests always allows me to think about design and so much more.
A simple illustration of Red/Green/Refactor
import React from "react";
import { shallow, mount} from "enzyme"
import {expect} from "chai"
import Main from "../containers/Main"
describe('<Main>', function () {
it('should have an input for the email', function () {
const wrapper = shallow(<Main/>);
expect(wrapper.find('input')).to.have.length(1);
});
it('should have a button', function () {
const wrapper = shallow(<Main/>);
expect(wrapper.find('button')).to.have.length(1);
});
it('should have props for handleChange and handleClick', function () {
const wrapper = shallow(<Main/>);
expect(wrapper.props().handleChange).to.be.defined;
expect(wrapper.props().handleSubmit).to.be.defined;
});
});
A Simple Test Suite to Test the Main Component
This is a React Test using Mocha and Enzyme. In later articles, I’ll give a detailed tutorial on testing React components.
It turns green!!!
import React, { Component, PropTypes } from 'react';
import '../css/App.css';
class Main extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="form-group" >
<input
className="form-control" style={{width: 200}}
onChange={this.props.handleChange}
type = "text"/>
<button
onClick={this.props.handleSubmit}
className="btn-success btn"> Fetch
</button>
</div>
);
}
}
Main.propTypes = {
handleChange: PropTypes.func,
handleSubmit: PropTypes.func,
};
export default Main;
Refactor: There is this quote from Kent that says, “Make it work. Make it right. Make it fast”. Once your test is passing, you should improve on your code. You can use the concept of DRY to avoid code smells.
To read more on the red/green/refactor, you can take a look at this.
Testing Methods
A lot of people confuse testing methods with testing techniques. Here is a simple doodle explaining this. I started reading The Doodle Revolution by Sunni Brown and I’m trying to improve my doodling skills 😎.
Yeah, I know it’s the worst ever 😄, but hey if you can help with a better one, I’m up for it. Okay, back to testing.
Before delving into the different testing techniques, let me give a brief summary or overview of the doodle drawn above.
We have the various testing methods which include white-box(open-box), black-box(close-box), Gray, Adhoc, Agile to mention a few. I highlighted the major differences between the commonly used ones i.e Black-box and White-box, but I’ll be discussing more on the white-box testing techniques since it’s what I do most of the time.
White-box testing techniques
I will be using this simple code snippet to explain the Statement and Branch coverages.
statement1: let funThings = [];
statement2: if (funThings.length >= 4) {
statement3: console.log('I do these and a lot of things 🙂');
statement4: } else {
statement5: console.log('Lighten up!!');
statement6: }
Total number of statements: 6
TestCase: funThings = [‘dance’, ‘eat’, ‘watch animations’, ‘play chess’]
- Statement Coverage, as the name implies, is a method used to check if every line of code is executed at least once. It doesn’t check for the false condition.
Number of statements Executed: 4
Statement coverage = (Number of statements Executed/Total number of statements) * 100.
2. Branch Coverage can also be called Decision Coverage. Unlike the statement counterpart, it covers both the true and false conditions.
No of statements Executed: 6
Branch coverage = (Number of statements Executed/Total number of statements) * 100.
3. Path Coverage usually cares about how you execute each line of code. Statement and Branch coverage don’t really care about the path in which code is executed.
For example:
Statement1: let A, B, C;
Statement2: if (A) {
Statement3: console.log(`I am ${A}`);
Statement4 }
Statement5: if (B) {
Statement6: console.log(`I am ${B}`);
Statement7 }
Statement8: if (C) {
Statement9: console.log(`I am ${C}`);
Statement10 }
Total number of statements: 10
Total number of paths: 3
Some of the real world failures examples of errors uncovered by white box testing include but not limited to:
- data inputs compromising security
- sensitive data being exposed to unauthorized users
- incorrect implementations of security functionality
- unintended software behavior that has security implications
What Next?
In my next article, I’ll be writing a tutorial on testing React application using Mocha and Enzyme. To read more about testing, check out this.
your doodling is actually good ...