Basic TDD: Red-Green-Refactor Pattern

Syukri Mullia Adil P
7 min readJun 1, 2019

--

Hi, guys, welcome to my first post in Medium!

In this post, I want to share about one of basic pattern in TDD, the Red-Green-Refactor pattern, using Golang. Hope you can enjoy and learn something from it!

About Red-Green-Refactor

The goal of using this pattern in TDD is to have a clean code that works. Each stage in the pattern plays an essential step in achieving our goal.

The Red stage

Employing TDD means our tests drive us, not the other way around. The red stage is the stage that we define our tests first. The goal in this step is to have some tests for the functionality we want to have. The tests should be failing as we don’t have any implementation yet — that’s why we call this stage as “Red”.

The Green stage

After we have our tests in the Red stage, it’s time to write its implementation. The goal is to have our failing tests from the Red stage pass, in other words, to have them become “Green”. In order to keep our code clean, we only write implementation just to pass our tests. Don’t write fancy codes, yet.

Refactor

Now we have written implementation for our functionality, as well as the respecting tests. In this stage, we may make our code cleaner by doing some refactoring. If we think our code resulted in the Green or Red stage is dirty, this is the time to make it clean. Yes, we may write fancy code now, but keep in mind we don’t always need a fancy code. Just refactor up until we think our code is good, readable, and maintainable. The most important thing, we should keep the tests green!

This pattern is more of a cycle. We may repeat the cycle every time we add more functionality in our code.

Example: An Article Model

I will show an example of using the Red-Green-Refactor pattern, a very simple one. This example will be written in Golang.

Let’s say we want to develop a kind of Blog where we can write posts/articles in it, just like in Medium but very much simpler. In doing so, we will need the model of the posts/articles. We will try to develop this model, let’s call it Article model, using TDD and its Red-Green-Refactor pattern.

First, we define functionality we want to have upon this Article model, or in Golang, Article struct:

  1. An empty constructor for the struct
  2. It stores values related to a post/article, like the title, the author, and the content. We also want to add some technical values in it such as the ID, createdAt, etc. These values will be represented as fields in the model.
  3. Some getters and setters for some fields

Now let’s get started! We will do it step by step, start from functionality number one, two, then three.

Let’s create a new file called article_test.go in a model package. First, we want to write the first test for our functionality number one: the constructor. The test for it will look like much more as follow:

func TestArticleModel(t *testing.T) {
var article *model.Article

t.Run("test_constructor", func(t *testing.T) {
article = model.NewArticle()

assert.NotNil(t, article, "article should not be nil")
})
}

The code above will result in a compile error as we don’t have model.Article and model.NewArticle() yet. Don’t worry, it is fine. In fact, this is what we wanted to have. Remember, the first stage in the pattern is the Red Stage. In this stage, we are free to define how we want to call/use the functions/models we are about to have. Sometimes, it will cause some reds, so that’s okay.

In order to achieve the next stage, that is the Green Stage, we need to do something so that our reds become green. How’d we do that? Of course, we start to do the implementation of model.Article and model.NewArticle().

We create a new file article.go and write our implementation code there.

type Article struct {
}

func NewArticle() *Article {
return &Article{}
}

Let’s stop right here for a second.

The implementation looks silly, right? We don’t have any field in the Article model. Remember, we are doing this step by step and we aimed to complete the functionality number one first, that is for the constructor. This code is just enough for that. Our previous test that was red, now it is green. That is all it takes for the Green Stage.

After achieving the Green Stage, it’s time to do some refactoring. In this step, what I notice is we can’t do any. That’s okay, we’ll just move the next functionality. As per this step, we have completed one cycle of Red-Green-Refactor in TDD. Congratulations!

Take a short break and put check mark upon our first functionality as it is done!

Let’s move to functionality number two: the fields. Let’s add a new test for fields in Article. Put this new test inside TestArticleModel.

t.Run("test_fields", func(t *testing.T) {
article = model.NewArticle()

_ = article.Id
_ = article.Title
_ = article.Content
_ = article.Author
_ = article.CreatedAt
_ = article.CreatedBy
_ = article.UpdatedAt
_ = article.UpdatedBy

assert.NotNil(t, article, "article should not be nil")
})

Again, this test will be red unless we add the fields in our Article struct. Let’s add them!

type Article struct {
Id int64
Title string
Content string
Author string
CreatedAt int64
CreatedBy string
UpdatedAt int64
UpdatedBy string
}

Now the test should be green. We can also do a bit refactoring in the test code. We can move out the initialization of article.

func TestArticleModel(t *testing.T) {
article := model.NewArticle()

t.Run("test_constructor", func(t *testing.T) {
assert.NotNil(t, article, "article should not be nil")
})

t.Run("test_fields", func(t *testing.T) {
_ = article.Id
_ = article.Title
_ = article.Content
_ = article.Author
_ = article.CreatedAt
_ = article.CreatedBy
_ = article.UpdatedAt
_ = article.UpdatedBy

assert.NotNil(t, article, "article should not be nil")
})
}

That’s it. We have completed another Red-Green-Refactor cycle just in a snap. Put another check mark upon functionality number two!

Finally, let’s move to our last functionality: the getters and setters!

Just like before, we add the tests first (red stage):

t.Run("test_getter", func(t *testing.T) {
assert.Empty(t, article.Id(), "id should be empty")
assert.Empty(t, article.Title(), "title should be empty")
assert.Empty(t, article.Content(), "content should be empty")
assert.Empty(t, article.Author(), "author should be empty")
assert.Empty(t, article.CreatedAt(), "createdAt should be empty")
assert.Empty(t, article.CreatedBy(), "createdBy should be empty")
assert.Empty(t, article.UpdatedAt(), "updatedAt should be empty")
assert.Empty(t, article.UpdatedBy(), "updatedBy should be empty")
})

t.Run("test_setter", func(t *testing.T) {
article.SetTitle("a title")
article.SetContent("some contents")
article.SetAuthor("an author")
article.SetCreatedBy("a creator")
article.SetUpdatedBy("an updater")

assert.Equal(t, "a title", article.Title(), "title should be set")
assert.Equal(t, "some contents", article.Content(), "content should be set")
assert.Equal(t, "an author", article.Author(), "author should be set")
assert.Equal(t, "a creator", article.CreatedBy(), "createdBy should be set")
assert.Equal(t, "an updater", article.UpdatedBy(), "updatedBy should be set")
})

and then add the implementation code in article.go (green stage):

// ....previous implementation codefunc (a Article) Id() int64 {
return a.id
}

func (a Article) Title() string {
return a.title
}

func (a *Article) SetTitle(title string) {
a.title = title
}

func (a Article) Content() string {
return a.content
}

func (a *Article) SetContent(content string) {
a.content = content
}

func (a Article) Author() string {
return a.author
}

func (a *Article) SetAuthor(author string) {
a.author = author
}

func (a Article) CreatedAt() int64 {
return a.createdAt
}

func (a Article) CreatedBy() string {
return a.createdBy
}

func (a *Article) SetCreatedBy(createdBy string) {
a.createdBy = createdBy
}

func (a Article) UpdatedAt() int64 {
return a.updatedAt
}

func (a Article) UpdatedBy() string {
return a.updatedBy
}

func (a *Article) SetUpdatedBy(updatedBy string) {
a.updatedBy = updatedBy
}

This time we can do some refactoring to keep our code clean. I notice that because we have getters and setters, we no longer need our fields to be public. Thus the test_fields we previously created is no longer needed and can be safely removed after we set our fields in Article to be private:

type Article struct {
id int64
title string
content string
author string
createdAt int64
createdBy string
updatedAt int64
updatedBy string
}

Our final code will look like this:

article_test.go

package model_test

import (
"testing"

"github.com/pegasus37/blogpost-backend/app/model"
"github.com/stretchr/testify/assert"
)

func TestArticleModel(t *testing.T) {
article := model.NewArticle()

t.Run("test_new_article", func(t *testing.T) {
assert.NotNil(t, article, "article should not be nil")
})

t.Run("test_getter", func(t *testing.T) {
assert.Empty(t, article.Id(), "id should be empty")
assert.Empty(t, article.Title(), "title should be empty")
assert.Empty(t, article.Content(), "content should be empty")
assert.Empty(t, article.Author(), "author should be empty")
assert.Empty(t, article.CreatedAt(), "createdAt should be empty")
assert.Empty(t, article.CreatedBy(), "createdBy should be empty")
assert.Empty(t, article.UpdatedAt(), "updatedAt should be empty")
assert.Empty(t, article.UpdatedBy(), "updatedBy should be empty")
})

t.Run("test_setter", func(t *testing.T) {
article.SetTitle("a title")
article.SetContent("some contents")
article.SetAuthor("an author")
article.SetCreatedBy("a creator")
article.SetUpdatedBy("an updater")

assert.Equal(t, "a title", article.Title(), "title should be set")
assert.Equal(t, "some contents", article.Content(), "content should be set")
assert.Equal(t, "an author", article.Author(), "author should be set")
assert.Equal(t, "a creator", article.CreatedBy(), "createdBy should be set")
assert.Equal(t, "an updater", article.UpdatedBy(), "updatedBy should be set")
})
}

article.go

package model

type Article struct {
id int64
title string
content string
author string
createdAt int64
createdBy string
updatedAt int64
updatedBy string
}

func NewArticle() *Article {
return &Article{}
}

func (a Article) Id() int64 {
return a.id
}

func (a Article) Title() string {
return a.title
}

func (a *Article) SetTitle(title string) {
a.title = title
}

func (a Article) Content() string {
return a.content
}

func (a *Article) SetContent(content string) {
a.content = content
}

func (a Article) Author() string {
return a.author
}

func (a *Article) SetAuthor(author string) {
a.author = author
}

func (a Article) CreatedAt() int64 {
return a.createdAt
}

func (a Article) CreatedBy() string {
return a.createdBy
}

func (a *Article) SetCreatedBy(createdBy string) {
a.createdBy = createdBy
}

func (a Article) UpdatedAt() int64 {
return a.updatedAt
}

func (a Article) UpdatedBy() string {
return a.updatedBy
}

func (a *Article) SetUpdatedBy(updatedBy string) {
a.updatedBy = updatedBy
}

Conclusion

The Red-Green-Refactor is one of basic pattern we can employ if we are using TDD. By using this pattern, stage by stage, cycle by cycle, we will have a code (both implementation and test code) that is clean and works. Don’t forget to keep practicing to master and get used to this pattern.

If you have any feedback, please kindly put them below. See you in my next post!

--

--