Love and Hate Relationship with TDD

Reyhan Alhafizal
4 min readMar 9, 2020

--

Source: https://www.testim.io/blog/scrum-testing-guide/

Test Driven Development (TDD) is an software engineering approach that requires unit tests to be written before the code to fulfill that and refactoring.

“Why would I make tests first?”

Some says the goal of TDD is specification and not validation. So it is a way to think through your requirements or design before your write your implementation code. Others says TDD is a programming technique to write clean code that works.

“What’s wrong with traditional testing?”

The rise of Agile movement leads a new practices at providing client with the program that meets hi need as quickly as possible. With this approach, traditional testing based on the V-Model offers unit tests to be written after the application code. This approach introducing confirmation bias as test written to adapt to the code. In the worst case, these test are not even written the code seems to work correctly.

Meanwhile, TDD offers unit tests to be written before the application code to guarantee all the specification are implemented in the cleanest way. If done correctly, programmers can achieve 100% coverage tests.

If it’s worth building, it’s worth testing.

If it’s not worth testing, why are you wasting your time working on it?

“How can I make test out of…nothing?”

Calm down, it’s not as difficult as you think. TDD has 3 phases:

  1. RED: Write a unit test in failure. The impossibility of compiling is also a failure.
  2. GREEN: Write the implementation code in the simplest way as soon as possible to pass the test. No need to implement the best way to do it.
  3. REFACTOR: Modify both implementation code and test code (if necessary) to eliminates code smells. Make sure the tests still passed after refactoring.
TDD Cycle | Source: https://hackernoon.com/introduction-to-test-driven-development-tdd-61a13bc92d92

You can create test from given requirements. Implement that requirements and then refactor. Repeat this process until all requirements tested and implemented.

“Nah, I hate it.”

Understandable. Maybe, this is a new paradigm for you. Learning a new paradigm is never easy. Give it a try and slowly you will earn benefits of TDD and love it eventually.

TDD at PPL

At PPL (Software Project Course), we — CSUI Students — encouraged to use TDD approach on our project. We firstly introduced with TDD in PPW (Web Development Course). Until now, it’s still difficult to implement TDD ideally. It’s because we often doesn’t really know how the implementation code will be. The other reason is sometimes we still learn the language or framework used in the project so “test-first” approach is unimaginable. This is the main issue in our team because we still learning using Node.js and all library it needs. As time goes, I try to implement TDD correctly and it’s gets better each time I use it.

For example, when I create Superuser model, I create unit test first.

# __test__/admin/admin.unit.test.jsconst db = require('../../db/models');const Superuser = db.superuser;
// const { sequelize } = db;
/**
* Check if given Superuser instance has expected attribute
* @param {Superuser} aSuperuser checked instance of superuser
* @param {JSON} superuserData expected data of superuser
*/
function checkCorrectAttribute(aSuperuser, superuserData) {
expect(aSuperuser.get('email')).toBe(superuserData.email);
expect(aSuperuser.get('role')).toBe(superuserData.role);
expect(aSuperuser.get('encryptedPassword'))
.not.toBe('superuserData'.encryptedPassword);
}
describe('Superuser/Admin unit test', () => {
const superuserData = {
email: 'aSuperuser@testing.com',
encryptedPassword: 'testing123',
role: 'admin',
};
it('should create an instance', async () => {
const aSuperuser = await Superuser.create(superuserData);
expect(aSuperuser).not.toBeNull();
checkCorrectAttribute(aSuperuser, superuserData);
});
}

In this test, I want a model named Superuser that has email, role, and encrypted password. Of course this test will be failed because I don’t even create Superuser model yet.

After that, I create the model.

const bcrypt = require('bcrypt');module.exports = (sequelize, DataTypes) => {
const Superuser = sequelize.define('superuser', {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
encryptedPassword: {
type: DataTypes.STRING,
allowNull: false,
},
role: {
type: DataTypes.ENUM('admin', 'restricted'),
allowNull: false,
},
}, {
freezeTableName: true,
hooks: {
beforeCreate: async (instance) => {
bcrypt.hash(instance.encryptedPassword, 10, (err, hash) => {
instance.set('encryptedPassword', hash);
});
},
},
});
return Superuser;
};

This model will encrypt the given password.

Now, the test will be passed. Next, you can refactor your model to be more efficient or cleaner. In TDD, you don’t have to create a big test first and implement a big code. You can always write a unit test, implement it and iterate it. In my case, you can write a test to check if Superuser model exists, Superuser can create model, has correct attribute, and finally check the model constraint.

Source:

http://agiledata.org/essays/tdd.html

--

--