DEV Community

Cover image for What is Test-Driven Development?
Dani Schuhman
Dani Schuhman

Posted on

What is Test-Driven Development?

Test-Driven Development, or TDD for short, is a software development process that focuses on writing test cases before you write actual code.

The idea is that you write a test, the tests fail for obvious reasons, and then you write the simplest code that will then make the code pass, then refactor that code, using the tests to make sure everything is preserved. Then you repeat the process all over again, building more tests and making things more complex. Sounds straightforward enough.

So why is it so important?

There are a lot of reasons why TDD is important. A 2005 study showed that programmers who used TDD were more productive. It's also thought to lead to less errors as you are testing as you create. It can lead to more modulated, flexible code, and more optimized code. It can drive the design process of a program. The list goes on and on.

As a student in the Flatiron School, TDD was used for almost all of the work that we did. I found, personally, that when learning to code, it's much easier to help break down a problem into little pieces and work on things one by one. It's much easier to tackle a task that's made up of twenty-five parts, by chipping away at it bit by bit, rather than trying to handle it all at once. Plus, there is something very satisfying of running tests and watching them change from red to green.

All of that is very well enough, but how do you go about using TDD if you haven't before?

There are a lot of wonderful resources on the web, that can talk you through how to set up the frameworks that are used to help drive TDD. What framework you use, depends on what language you use as well. At the moment, I am only familiar with a couple, and can't talk more about how the other ones work and how to specifically write tests for everything mentioned here. But I hope to be able to one day.

Here are some popular ones that are commonly used:

  1. RSpec - Behavior driven development for Ruby.
  2. Pytest - An open source tool used in Python.
  3. Junit 5 - A programmer-friendly testing framework for Java and the JVM.
  4. Mocha - A feature-rich JavaScript testing framework running on Node.js and in the browser.
  5. Jest - A JavaScript testing framework that works with projects using Babel, TypeScript, Node, React, Angular, Vue and more.
  6. csUnit - An open source testing tool for the .NET framework.

But what does TDD look like when implemented if you've never seen a test before?

With a language like RSpec, a test could look like the following, for a User model:

require 'rails_helper'

RSpec.describe User, :type => :model do 
    let(:user) {
        User.create(
        :email => "me@mail.com",
        :user_name => "SomeGuy",
        :password => "123ABC",
        :id => 4
        )
    }

    it "is valid with a username, password, and email" do 
        expect(user).to be_valid
    end 

    it "is not valid without a password" do 
        expect(User.new(user_name: "Name")).not_to be_valid
    end 

    it "is not valid without a email" do 
        expect(User.new(password: "Password")).not_to be_valid
    end 

    it "is not valid without a username" do 
        expect(User.new(email: "Boo@mail.com")).not_to be_valid
    end 
end 
Enter fullscreen mode Exit fullscreen mode

For a language like pytest, it can look like the following:

# content of test_sample.py
def inc(x):
    return x + 1


def test_answer():
    assert inc(3) == 5
Enter fullscreen mode Exit fullscreen mode

Junit:

import static org.junit.jupiter.api.Assertions.assertEquals;

import example.util.Calculator;

import org.junit.jupiter.api.Test;

class MyFirstJUnitJupiterTests {

    private final Calculator calculator = new Calculator();

    @Test
    void addition() {
        assertEquals(2, calculator.add(1, 1));
    }

}
Enter fullscreen mode Exit fullscreen mode

Mocha:

var assert = require('assert');
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

Jest:

const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});
Enter fullscreen mode Exit fullscreen mode

csUnit:

using System;

using System.Collections.Generic;

using System.Text;



using csUnit;



namespace Go48.Core {

   [TestFixture]

   public class CalculatorTests {

      [Test]

      public void AddTwoNumbers() {

         // Step 1: Set up some objects

         Calculator calculator = new Calculator();



         // Step 2: Manipulate the objects

         calculator.Enter(3);

         calculator.Enter(5);

         calculator.Add();



         // Step 3: Assert outcome is correct

         Assert.Equals(8, calculator.Top);

      }

   }

}
Enter fullscreen mode Exit fullscreen mode

Conclusion

The common thread through all of these tests is that as a programmer, you want something to exist. Maybe it's a function that returns an array, maybe it's a function that performs some kind of mathematic operation, maybe it's creating a User model so that people can log in and out of an application, etc.

The point is that we want the code to do something, so tests are written first. Then the code is crafted to pass those tests. And then code is refactored, more tests are written, and what started out as a simple operation starts to expand into something more, until something is a full fledged application and hopefully, doesn't have nine-zillion bugs that need to be fixed before it can be used.

If you've never encountered TDD before, I hope that this brief introduction to the practice explains things enough, that maybe you'll click on the links of the language of your choice, follow a tutorial, and try writing your first test today!

Top comments (0)