Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Go Design Patterns

You're reading from   Go Design Patterns Best practices in software development and CSP

Arrow left icon
Product type Paperback
Published in Feb 2017
Publisher Packt
ISBN-13 9781786466204
Length 402 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Mario Castro Contreras Mario Castro Contreras
Author Profile Icon Mario Castro Contreras
Mario Castro Contreras
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Ready... Steady... Go! FREE CHAPTER 2. Creational Patterns - Singleton, Builder, Factory, Prototype, and Abstract Factory Design Patterns 3. Structural Patterns - Composite, Adapter, and Bridge Design Patterns 4. Structural Patterns - Proxy, Facade, Decorator, and Flyweight Design Patterns 5. Behavioral Patterns - Strategy, Chain of Responsibility, and Command Design Patterns 6. Behavioral Patterns - Template, Memento, and Interpreter Design Patterns 7. Behavioral Patterns - Visitor, State, Mediator, and Observer Design Patterns 8. Introduction to Gos Concurrency 9. Concurrency Patterns - Barrier, Future, and Pipeline Design Patterns 10. Concurrency Patterns - Workers Pool and Publish/Subscriber Design Patterns

Testing and TDD

When you write the first lines of some library, it's difficult to introduce many bugs. But once the source code gets bigger and bigger, it becomes easier to break things. The team grows and now many people are writing the same source code, new functionality is added on top of the code that you wrote at the beginning. And code stopped working by some modification in some function that now nobody can track down.

This is a common scenario in enterprises that testing tries to reduce (it doesn't completely solve it, it's not a holy grail). When you write unit tests during your development process, you can check whether some new feature is breaking something older or whether your current new feature is achieving everything expected in the requirements.

Go has a powerful testing package that allows you also to work in a TDD environment quite easily. It is also very convenient to check the portions of your code without the need to write an entire main application that uses it.

The testing package

Testing is very important in every programming language. Go creators knew it and decided to provide all libraries and packages needed for the test in the core package. You don't need any third-party library for testing or code coverage.

The package that allows for testing Go apps is called, conveniently, testing. We will create a small app that sums two numbers that we provide through the command line:

func main() { 
    //Atoi converts a string to an int 
    a, _ := strconv.Atoi(os.Args[1]) 
    b, _ := strconv.Atoi(os.Args[2]) 
 
    result := sum(a,b) 
    fmt.Printf("The sum of %d and %d is %d\n", a, b, result) 
} 
 
func sum(a, b int) int { 
    return a + b 
} 

Let's execute our program in the terminal to get the sum:

$ go run main.go 3 4
The sum of 3 and 4 is 7

By the way, we're using the strconv package to convert strings to other types, in this case, to int. The method Atoi receives a string and returns an int and an error that, for simplicity, we are ignoring here (by using the underscore).

Tip

You can ignore variable returns by using the underscores if necessary, but usually, you don't want to ignore errors.

Ok, so let's write a test that checks the correct result of the sum. We're creating a new file called main_test.go. By convention, test files are named like the files they're testing plus the _test suffix:

func TestSum(t *testing.T) { 
    a := 5 
    b := 6 
    expected := 11 
 
    res := sum(a, b) 
    if res != expected { 
        t.Errorf("Our sum function doens't work, %d+%d isn't %d\n", a, b, res) 
    } 
} 

Testing in Go is used by writing methods started with the prefix Test, a test name, and the injection of the testing.T pointer called t. Contrary to other languages, there are no asserts nor special syntax for testing in Go. You can use Go syntax to check for errors and you call t with information about the error in case it fails. If the code reaches the end of the Test function without arising errors, the function has passed the tests.

To run a test in Go, you must use the go test -v command (-v is to receive verbose output from the test) keyword, as following:

$ go test -v
=== RUN   TestSum
--- PASS: TestSum (0.00s)
PASS
ok   github.com/go-design-patterns/introduction/ex_xx_testing 0.001s

Our tests were correct. Let's see what happens if we break things on purpose and we change the expected value of the test from 11 to 10:

$ go test
--- FAIL: TestSum (0.00s)
    main_test.go:12: Our sum function doens't work, 5+6 isn't 10
FAIL
exit status 1
FAIL  github.com/sayden/go-design-patterns/introduction/ex_xx_testing 0.002s

The test has failed (as we expected). The testing package provides the information you set on the test. Let's make it work again and check test coverage. Change the value of the variable expected from 10 to 11 again and run the command go test -cover to see code coverage:

$ go test -cover
PASS
coverage: 20.0% of statements
ok  github.com/sayden/go-design-patterns/introduction/ex_xx_testing 0.001s

The -cover options give us information about the code coverage for a given package. Unfortunately, it doesn't provide information about overall application coverage.

What is TDD?

TDD is the acronym for Test Driven Development. It consists of writing the tests first before writing the function (instead of what we did just before when we wrote the sum function first and then we wrote the test function).

TDD changes the way to write code and structure code so that it can be tested (a lot of code you can find in GitHub, even code that you have probably written in the past is probably very difficult, if not impossible, to test).

So, how does it work? Let's explain this with a real life example--imagine that you are in summer and you want to be refreshed somehow. You can build a pool, fill it with cold water, and jump into it. But in TDD terms, the steps will be:

  1. You jump into a place where the pool will be built (you write a test that you know it will fail).
  2. It hurts... and you aren't cool either (yes... the test failed, as we predicted).
  3. You build a pool and fill it with cold water (you code the functionality).
  4. You jump into the pool (you repeat the point 1 test again).
  5. You're cold now. Awesome! Object completed (test passed).
  6. Go to the fridge and take a beer to the pool. Drink. Double awesomeness (refactor the code).

So let's repeat the previous example but with a multiplication. First, we will write the declaration of the function that we're going to test:

func multiply(a, b int) int { 
    return 0 
} 

Now let's write the test that will check the correctness of the previous function:

import "testing" 
 
func TestMultiply(t *testing.T) { 
    a := 5 
    b := 6 
    expected := 30 
 
    res := multiply(a, b) 
    if res != expected { 
        t.Errorf("Our multiply function doens't work, %d*%d isn't %d\n", a, b, res) 
    } 
} 

And we test it through the command line:

$ go test
--- FAIL: TestMultiply (0.00s)
main_test.go:12: Our multiply function doens't work, 5+6 isn't 0
FAIL
exit status 1
FAIL    github.com/sayden/go-designpatterns/introduction/ex_xx_testing/multiply    

0.002s

Nice. Like in our pool example where the water wasn't there yet, our function returns an incorrect value too. So now we have a function declaration (but isn't defined yet) and the test that fails. Now we have to make the test pass by writing the function and executing the test to check:

func multiply(a, b int) int { 
    return a*b 
} 

And we execute again our testing suite. After writing our code correctly, the test should pass so we can continue to the refractoring process:

$ go test
PASS
ok      github.com/sayden/go-design-patterns/introduction/ex_xx_testing/multiply    
0.001s

Great! We have developed the multiply function following TDD. Now we must refactor our code but we cannot make it more simple or readable so the loop can be considered closed.

During this book, we will write many tests that define the functionality that we want to achieve in our patterns. TDD promotes encapsulation and abstraction (just like design patterns do).

You have been reading a chapter from
Go Design Patterns
Published in: Feb 2017
Publisher: Packt
ISBN-13: 9781786466204
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime