DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Enterprise AI Trend Report: Gain insights on ethical AI, MLOps, generative AI, large language models, and much more.

2024 Cloud survey: Share your insights on microservices, containers, K8s, CI/CD, and DevOps (+ enter a $750 raffle!) for our Trend Reports.

PostgreSQL: Learn about the open-source RDBMS' advanced capabilities, core components, common commands and functions, and general DBA tasks.

AI Automation Essentials. Check out the latest Refcard on all things AI automation, including model training, data security, and more.

Related

  • Improving Customer-Facing App Quality Using Tricentis Testim
  • Best Practices for Writing Unit Tests: A Comprehensive Guide
  • Testing in DevOps – The Basic and Critical Things You Need to Know
  • Comparison of Various AI Code Generation Tools

Trending

  • DZone's Cloud Native Research: Join Us for Our Survey (and $750 Raffle)!
  • PostgresML: Streamlining AI Model Deployment With PostgreSQL Integration
  • OWASP Top 10 Explained: SQL Injection
  • Data Flow Diagrams for Software Engineering
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Exploring Unit Testing in Golang

Exploring Unit Testing in Golang

Unit Testing is a must for any successful implementation. Let's explore how to write unit testing in Golang using built-in Golang features.

By 
Narasimha Rao Konangi user avatar
Narasimha Rao Konangi
·
Jun. 27, 23 · Tutorial
Like (3)
Save
Tweet
Share
4.2K Views

Join the DZone community and get the full member experience.

Join For Free


Unit testing is a fundamental practice in software development that focuses on testing individual units of code, typically at the function or method level. A unit is the smallest testable part of an application, such as a function, method, or class. The objective of unit testing is to verify that each unit of code works as expected in isolation.

Here Are Some Key Aspects of Unit Testing

Isolation

Unit tests are designed to be independent and isolated from other units of code. This means that each unit(a.k.a functions, methods) is tested in isolation, without dependencies on other units or external resources. This is usually achieved by using test doubles, such as mock objects or stubs, to simulate the behavior of dependencies.

Automation

Unit tests are typically automated, which means they can be executed repeatedly and reliably. Automated unit tests can be written using a testing framework or library that provides tools for defining test cases, running tests, and asserting expected results.

Granularity

Unit tests focus on testing small, cohesive units of code. This allows for fine-grained testing and helps identify issues at an early stage. By testing individual units, developers can pinpoint specific areas of code that are not functioning correctly.

Coverage

Unit tests aim to achieve high code coverage, meaning that as much of the code as possible is tested by unit tests. This helps ensure that all the branches, conditions, and edge cases of the code are exercised and validated.

Fast Execution

Unit tests should execute quickly, allowing developers to get rapid feedback on the correctness of their code. Fast execution helps maintain developer productivity and encourages running tests frequently, such as after each code change.

Test-Driven Development (TDD)

Unit testing is often associated with Test-Driven Development. In TDD, developers write the unit tests before writing the code itself. This practice helps drive the development process, as developers focus on writing code that fulfills the requirements defined by the tests.

Benefits of Unit Testing

  • Early Bug Detection: Unit tests can catch bugs and issues at an early stage, enabling developers to fix them before they propagate to other parts of the system.
  • Improved Code Quality: Unit testing encourages developers to write modular, well-structured code that is easier to test and maintain.
  • Facilitates Refactoring: Unit tests provide a safety net when refactoring code. They ensure that changes made to the codebase do not break existing functionality.
  • Documentation and Code Examples: Unit tests serve as living documentation and code examples that demonstrate how to use and interact with units of code.

Unit Testing in Golang 

Let's take a look at the unit testing in Golang. For any development project, there is a possibility of introducing bugs unintentionally. Having a comprehensive list of test cases with different variances would help to discover these bugs well in advance. In fact, the best-recommended way of writing code is by creating a unit test first before start implementing the actual code unit. Go language provides a built-in package for writing unit tests called "testing." Developers can simply import this package and start creating their unit test cases.

Built-in Testing Package in Golang

The testing package is the backbone of Golang unit testing. It helps developers to create unit tests with different types of test functions. The testing.T type offers methods to control test execution, such as running tests in parallel with Parallel(), skipping tests with Skip(), and calling a test teardown function with Cleanup().

Errors and Logs

The testing.T type provides various practical tools to interact with the test workflow:

  • t.Fail*(), which helps to indicate the failure of the test case execution. It provides very limited details compared to t.Error*().
  • t.Errorf(), which prints out an error message and sets the test as failed. t.Error* does not stop the execution of the test. Instead, all encountered errors will be reported once the test is completed.
  • t.Fatal*()  It should be used to fail the execution, and it is beneficial in scenarios where it's the requirement to exist the test execution.
  • t.Log*() function is used to print information during the test execution; it can be handy in some situations.

Conventions of Unit Testing in Go Language 

  • “testing”  is the name of the built-in package for supporting unit test creations.
  • “go test”  It is the command for executing tests quickly.
  • Unit tests are written in separate files that are named with “_test.go”. For example, a given “Operations.go” source code will have a unit test file name  “Operations_test.go” with the list of test cases written in it.
  • All Unit Tests are placed in the same package along with the actual code.
  • Go Unit test functions start with Test and are appended with the function name. Ex: “TestFuncName()”
  • Unit Test Function always has only one parameter *testing.T  Ex: TestFuncName(test *testing.T)
  • test.Error,test.Fail, test.Fatal Are the calls used to indicate test errors or failures.

Simple Functionality to Implement 

Start with a simple use case of finding the difference between two integer numbers. This difference must be a positive number. 

  • The difference between 5 and 2 is 3. 
  • The difference between 8 and 2 is 6. 
  • The difference between 3 and 9 is 6. 
  • The difference between 0 and 2 is  2.
  • The difference between 4 and 0 is 4.

Let's create operations.go file with a simple Difference(num1, num2) function, which returns the actual difference between those two numbers. 

Go
 
package mathops

// Difference function that finds the difference between two integers. 
// It returns positive numbers always
func Difference(num1, num2 int) (difference int) {
  if num1 > num2{
      return num1 - num2
 	}else{
	Return num2 - num1
	}
}


Writing Unit Test Cases

In the Test-driven development approach, unit tests are written well before writing the actual code. For this demonstration, Let's create unit test cases for the above function. All Unit Test functions are written in a separate file that has a file name ending with _test.go. For this case, let's create operations_test.go with the Unit test function as TestDifference(test *testing.T).

All Go unit test files _test.goare typically placed in the same package along with the actual source code files. Also, the Go compiler will take care of excluding these _test.go files during the build process.

Create operations_test.go

Go
 
package mathops
import "testing"

func TestDifference(test *testing.T) {
	// Test 1: Positive
	actual := Difference(4, 6)
	expected := 2
	if actual != expected {
		test.Errorf("actual value: %d, expected value: %d", actual, expected)
	} else {
		test.Logf("actual value: %d, expected value: %d", actual, expected)
	}
}


Testing using go test command. PASS.

Testing using go test command.

Testing using go test command. FAIL.

Testing using go test command. FAIL.


Table Driven Testing 

So far, It's a single test for each test function. This would lead to a lot of code to maintain for multiple unit test cases. This is a better way to organize various unit test scenarios using Table driven unit testing approach in Go lang.  

The table-driven test approach starts with defining the input data structure. It's like describing the column of the table.  

Go
 
type diffTest struct {
  name  string
  num1, num2, expected int
}


Based on this data structure, Developer should create test data set as a predefined variable. Each row of the table lists a test case to execute. Once the table is defined, you write the execution loop.

Go
 
var diffTests = []diffTest{
	{"1st Test: values 2,3 ", 2, 3, 1},
	{"2nd Test: values 4,8 ", 4, 8, 3},
	{"3rd Test: values 6,9 ", 6, 9, 3},
	{"4th Test: values 10,13 ", 10, 13, 3},
}


The execution loop calls, which defines a subtest. As a result, each row of the table defines a subtest named. [NameOfTheFuction]/[NameOfTheSubTest].

This way of writing tests is very popular and considered the canonical way to write unit tests in Go. 

Let's Take a look at the following sample:

Go
 
package mathops

import "testing"

type diffTest struct {
	name                 string
	num1, num2, expected int
}

var diffTests = []diffTest{
	{"1st Test: values 2,3 ", 2, 3, 1},
	{"2nd Test: values 4,8 ", 4, 8, 3},
	{"3rd Test: values 6,9 ", 6, 9, 3},
	{"4th Test: values 10,13 ", 10, 13, 3},
}

func TestDifference(t *testing.T) {
	for _, test := range diffTests {
		t.Run(test.name, func(t *testing.T) {
			actual := Difference(test.num1, test.num2)
			if actual != test.expected {
				t.Errorf(test.name, " FAIL: actual %d not equal to expected %d", actual, test.expected)
			} else {
				t.Logf(test.name, " PASS : actual %d not equal to expected %d", actual, test.expected)
			}
		})
	}
}


Sample Execution:


Code Coverage

Writing unit test cases is the initial stride toward attaining quality. One can gauge the extent of code covered by these unit tests. Developers should strive to achieve comprehensive code coverage to uncover defects during testing effectively. Golang offers a built-in feature for determining the code coverage of unit tests.

To achieve more coverage, developers must include all possible scenarios to help unit tests execute complete code.

Following unit tests helps to achieve 100% code coverage for the sample code:

unit tests helps to achieve 100% code coverage for the sample code.


The"testing"package in Golang provides several additional features for unit testing. Here are a few notable ones:

  • Parallel Execution: By default, tests are run sequentially, but you can use the t.Parallel() Method to indicate that a test should be executed in parallel. Tests calling this method will be paused and resumed in parallel once the non-parallel tests have finished executing.
  • Skip Unit Tests: The t.Skip() method allows you to distinguish between unit tests and integration tests. Integration tests typically validate multiple functions and components together and are slower to execute. Using, you can choose to run only the unit tests when necessary.
  • Test Tear Down with Cleanup: The t.Cleanup() method offers a convenient way to manage test teardown. It ensures that the specified function is executed at the end of each test, including subtests. This helps communicate the intended behavior to anyone reading the test.
  • Benchmark Testing: Benchmark tests evaluate the performance of your code. These tests measure the runtime and memory usage of an algorithm by executing the same function multiple times. They provide insights into the efficiency of your code.
  • Fuzz Testing: Fuzz testing is an exciting technique that uses random input to uncover bugs or identify edge cases. Go's fuzzing algorithm intelligently generates multiple input combinations to cover as many statements in your code as possible.

Conclusion

In conclusion, testing is crucial for validating the logic of your code and identifying bugs during development. Golang offers a comprehensive set of tools for testing your applications out of the box.

Golang Software development Test-driven development Execution (computing) Go (programming language) unit test

Opinions expressed by DZone contributors are their own.

Related

  • Improving Customer-Facing App Quality Using Tricentis Testim
  • Best Practices for Writing Unit Tests: A Comprehensive Guide
  • Testing in DevOps – The Basic and Critical Things You Need to Know
  • Comparison of Various AI Code Generation Tools

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: