Skip to main content

Set up a Temporal Application project - Go SDK dev guide

The first step to creating a new Temporal Application is to set up your development environment. This chapter walks through the steps to do that using the Go SDK.

Construct a new Temporal Application project

This chapter of the Temporal Go SDK developer's guide covers the minimum set of concepts and implementation details needed to build and run a Temporal Application using Go.

By the end of this section you will know how to construct a new Temporal Application project.

Learning objectives:

  • Describe the tools available and recommended to develop Workflows.
  • Describe the code that actually forms a Temporal application.
  • Implement an appropriate testing framework.

Much of the information in this chapter is also covered in the Temporal 101 course

This chapter introduces the Background Check use case and a sample application as a means to contextualize the information. Future developer guide chapters build on this use case and sample application.

There are three ways to follow this guide:

Read more in the Choose a development Cluster section.

In this chapter you will do the following:

  1. Download the Temporal CLI.
  2. Choose your development Cluster.
  3. Create a Namespace on your development Cluster.
  4. Copy boilerplate code into your IDE.
  5. Run your the Worker.
  6. Start the Workflow using the CLI.
  7. Explore the Web UI to view the status of the Workflow and confirm polling Workers.
  8. Add a testing framework and unit tests to the application
  9. Run the application unit tests

Install the Temporal CLI

How to download and install the Temporal CLI

The Temporal CLI is available on MacOS, Windows, and Linux.

MacOS

How to install the Temporal CLI on Mac OS

Choose one of the following install methods to install the Temporal CLI on MacOS:

Install the Temporal CLI with Homebrew

brew install temporal

Install the Temporal CLI with cURL

curl -sSf https://temporal.download/cli.sh | sh

Install the Temporal CLI from CDN

  1. Select the platform and architecture needed.
  1. Extract the downloaded archive.

  2. Add the temporal binary to your PATH.

Linux

How to install the Temporal CLI on Linux

Choose one of the following install methods to install the Temporal CLI on Linux:

Install the Temporal CLI with cURL

curl -sSf https://temporal.download/cli.sh | sh

Install the Temporal CLI from CDN

  1. Select the platform and architecture needed.
  1. Extract the downloaded archive.

  2. Add the temporal binary to your PATH.

Windows

How to install the Temporal CLI on Windows

Follow these instructions to install the Temporal CLI on Windows:

Install the Temporal CLI from CDN

  1. Select the platform and architecture needed and download the binary.
  1. Extract the downloaded archive.

  2. Add the temporal.exe binary to your PATH.

Choose a development Cluster

Which development Cluster should you choose?

We recommend choosing a development environment based on your requirements.

The source code for the Temporal Server (the orchestrating component of the Temporal Cluster) is licensed under the MIT open source license. So, in theory, anyone can take the Temporal Server code and run their Temporal Platform in any number of creative ways.

However, for most developers we recommend starting by choosing one of the following:

Temporal does not directly run your code

Keep in mind that in every scenario, the “Temporal Platform” does not host and run your Workers (application code). It is up to you, the developer, to host your application code. The Temporal Platform ensures that properly written code durably executes in the face of platform-level failures.

Local dev server

When to use a local development server?

We recommend using the local development server if you are new to Temporal, or want to start something from scratch and don’t have a self-hosted environment ready or want to pay for a Temporal Cloud account.

The Temporal CLI comes bundled with a development server and provides a fast way to start running Temporal Applications.

However, the local development server does not emit any metrics. If you are eager to to set up Cluster-level metrics for performance tuning, we recommend using a self-hosted Cluster or Temporal Cloud.

Start the dev server

How to start a local development server

If you have successfully installed the Temporal CLI, open a new terminal and run the following command:

temporal server start-dev

This command automatically starts the Temporal Web UI, creates a default Namespace, and creates an in-memory database.

The Temporal Web UI serves to http://localhost:8233.

For more command details and options, see the CLI reference

Create a custom Namespace

How to create a Namespace on the development server

The development server does automatically create a default Namespace (named "default") when it starts up. However, you will create a custom one for our application. Since this is something recommended at a production level, it's recommend practicing it with the development server.

Use the temporal operator namespace create command using the Temporal CLI to create a Namespace on the development server.

temporal operator namespace create backgroundcheck_namespace

For command details and options, see the CLI reference.

Temporal Cloud

When to use Temporal Cloud

If you do not have a Temporal Cloud Account, you can request one using the link on the Get started with Temporal Cloud guide.

We recommend starting off with Temporal Cloud if you already have a production use case, or need to move a scalable proof of concept into production.

In other words, Temporal Cloud is perfect if you are ready to run at scale and don’t want the overhead of managing your own self-hosted Cluster.

To create a Namespace in Temporal Cloud, follow the instructions in How to create a Namespace.

Safely store your certificate and private key

Store certificates and private keys generated for your Namespace as files or environment variables in your project. You need access to your certificate and key to run your Workers and start Workflows.

For more information on certificate requirements, see How to manage certificates in Temporal Cloud.

Self-hosted Temporal Cluster

We recommend using a self-hosted environment if you are starting something new and need to scale with production-level features, but don’t yet need or want to pay for Temporal Cloud.

For example, running a self-hosted Cluster lets you try different databases, view Cluster metrics, use custom Search Attributes, and even play with the Archival feature.

For the purposes of this guide, we show how to use a self-hosted environment that runs completely out of Docker. We acknowledge that it takes a fair amount of experience to elevate from a self-hosted environment in Docker to something that can run at an enterprise production scale. The self-hosted information in this guide should help you make more informed decisions.

To follow along with self-hosted parts of this guide, install the following:

Then, clone the temporalio/docker-compose repository.

Change directory into the root of the project.

Run the docker compose up command.

git clone https://github.com/temporalio/docker-compose.git
cd docker-compose
docker compose up

Create a command alias for the Temporal CLI:

alias temporal_docker="docker exec temporal-admin-tools temporal"

Create a Namespace.

temporal_docker operator namespace create backgroundcheck_namespace

Boilerplate Temporal Application project code

What is the minimum code I need to create a boilerplate Temporal Application?

Let’s start with a single Activity Workflow and register those functions with a Worker.

After we get the Worker running and have started a Workflow Execution, we will add a testing framework.

Project structure

You can organize Temporal Application code to suit various needs in a way that aligns with the idiomatic style of the language you are working in. This includes structuring your files according to your organization's best practices.

However, there are some general ways to think about organizing code.

The best practice is to group Workflows together, Activities together, and separate your Worker process into a standalone file. Often this happens respectively per use case, business process, or domain.

For monorepo-style organizational techniques, consider a designated Workflow directory for each use case and place each Workflow in its own file, but also maintain a dedicated place for shared Activities.

For example, your project structure could look like this:

/monorepo
/shared_activities
| payment.go
| send_email.go
/shared_tests
| tests.go
/backgroundcheck
/workflows
| backgroundcheck.go
/activities
| ssntrace.go
/worker
| main.go
/tests
| tests.go
/loanapplication
/workflows
| loanapplication.go
/activities
| creditcheck.go
/worker
| main.go
/tests
| tests.go

If you are following along with this guide, your project will look like this:

/backgroundcheck
/workflows
| backgroundcheck.go
/activities
| ssntrace.go
/worker
| main.go
/tests
| tests.go

Initialize Go project dependency framework

If you have created a similar project structure as noted earlier, run go mod init to create a new Go module for this project. The module name will be something like <your_name>/backgroundcheck:

mkdir backgroundcheck
cd backgroundcheck
go mod init github.com/your_name/backgroundcheck

Then, use go get to install the Temporal Go SDK:

go get go.temporal.io/sdk

Boilerplate Workflow code

In the Temporal Go SDK programming model, a Workflow Definition is an exportable function. The BackgroundCheck function below is an example of a basic Workflow Definition.

View the source code in the context of the rest of the application code.

package workflows

import (
"time"

"go.temporal.io/sdk/workflow"

"documentation-samples-go/dev-guide/chapters/setup/activities"
)


// BackgroundCheck is your custom Workflow Definition.
func BackgroundCheck(ctx workflow.Context, param string) (string, error) {
// Define the Activity Execution options
// StartToCloseTimeout or ScheduleToCloseTimeout must be set
activityOptions := workflow.ActivityOptions{
StartToCloseTimeout: 10 * time.Second,
}
ctx = workflow.WithActivityOptions(ctx, activityOptions)
// Execute the Activity synchronously (wait for the result before proceeding)
var ssnTraceResult string
err := workflow.ExecuteActivity(ctx, activities.SSNTraceActivity, param).Get(ctx, &ssnTraceResult)
if err != nil {
return "", err
}
// Make the results of the Workflow available
return ssnTraceResult, nil
}

The first parameter of a Go-based Workflow Definition must be of the workflow.Context type. It is used by the Temporal Go SDK to pass around Workflow Execution context, and virtually all the Go SDK APIs that are callable from the Workflow require it. It is acquired from the go.temporal.io/sdk/workflow package.

The workflow.Context entity operates similarly to the standard context.Context entity provided by Go. The only difference between workflow.Context and context.Context is that the Done() function, provided by workflow.Context, returns workflow.Channel instead of the standard Go chan.

Additional parameters can be passed to the Workflow when it is invoked. A Workflow Definition may support multiple custom parameters, or none. All Workflow Definition parameters must be serializable and can't be channels, functions, variadic, or unsafe pointers.

To spawn an Activity Execution, call ExecuteActivity() inside your Workflow Definition. The API is available from the go.temporal.io/sdk/workflow package. The ExecuteActivity() API call requires an instance of workflow.Context, the Activity function name, and any variables to be passed to the Activity Execution.

A Go-based Workflow Definition can return either just an error or a customValue, error combination. We get into the best practices around Workflow params and returns in the one of the next sections.

In regards to code organization, we recommend organizing Workflow code together with other Workflow code. For example, in a small project like this, it is still a best practice to have a dedicated file for each Workflow.

Boilerplate Activity code

In the Temporal Go SDK programming model, an Activity is an exportable function or a struct method. Below is an example of an Activity defined as a function.

View the source code in the context of the rest of the application code.

package activities

import (
"context"
)


// SSNTraceActivity is your custom Activity Definition.
func SSNTraceActivity(ctx context.Context, param string) (*string, error) {
// This is where a call to another service is made
// Here we are pretending that the service that does SSNTrace returned "pass"
result := "pass"
return &result, nil
}

The first parameter of an Activity Definition is context.Context. This parameter is optional for an Activity Definition, though it is recommended, especially if the Activity is expected to use other Go SDK APIs.

An Activity Definition can support as many other custom parameters as needed. However, all parameters must be serializable. For example, parameters can’t be channels, functions, variadic, or unsafe pointers.

Run a dev server Worker

To run a Worker Process with a local development server, define the following steps in code:

  • Initialize a Temporal Client.
  • Create a new Worker by passing the Client to creation call.
  • Register the application's Workflow and Activity functions.
  • Call run on the Worker.

In regards to organization, we recommend keeping Worker code separate from Workflow and Activity code.

View the source code in the context of the rest of the application code.

package main

import (
"log"

"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"

"documentation-samples-go/dev-guide/chapters/setup/activities"
"documentation-samples-go/dev-guide/chapters/setup/workflows"
)


func main() {
// Initialize a Temporal Client
// Specify the Namespace in the Client options
clientOptions := client.Options{
Namespace: "backgroundcheck_namespace",
}
temporalClient, err := client.Dial(clientOptions)
if err != nil {
log.Fatalln("Unable to create a Temporal Client", err)
}
defer temporalClient.Close()
// Create a new Worker
yourWorker := worker.New(temporalClient, "backgroundcheck-boilerplate-task-queue-local", worker.Options{})
// Register Workflows
yourWorker.RegisterWorkflow(workflows.BackgroundCheck)
// Register Activities
yourWorker.RegisterActivity(activities.SSNTraceActivity)
// Start the Worker Process
err = yourWorker.Run(worker.InterruptCh())
if err != nil {
log.Fatalln("Unable to start the Worker Process", err)
}
}
Auto restart worker when code changes

Use gow to automatically restart the Worker Process whenever any of the Go code files in your project change.

go install github.com/mitranim/gow@latest
gow worker/main.go # automatically restarts when the project files change

Run a Temporal Cloud Worker

A Temporal Cloud Worker requires that you specify the following in the Client connection options:

  • Temporal Cloud Namespace
  • Temporal Cloud Address
  • Certificate and private key associated with the Namespace
View the source code in the context of the rest of the application code.

package main

import (
"crypto/tls"
"log"
"os"

"github.com/joho/godotenv"

"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"

"documentation-samples-go/dev-guide/chapters/setup/activities"
"documentation-samples-go/dev-guide/chapters/setup/workflows"
)


func main() {
err := godotenv.Load(".env")
if err != nil {
log.Fatalln("Unable to load environment variables from file", err)
}
// Get the key and cert from your env or local machine
clientKeyPath := "./ca.key"
clientCertPath := "./ca.pem"
// Use the crypto/tls package to create a cert object
cert, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath)
if err != nil {
log.Fatalln("Unable to load cert and key pair.", err)
}
// Specify the host and port of your Temporal Cloud Namespace
// Host and port format: namespace.unique_id.tmprl.cloud:port
namespace := os.Getenv("TEMPORAL_CLOUD_NAMESPACE")
port := os.Getenv("TEMPORAL_CLOUD_PORT")
hostPort := namespace + ".tmprl.cloud:" + port
// Create a new Temporal Client
// Specify Namespace, Hostport and tls certificates in the ConnectionOptions
temporalClient, err := client.Dial(client.Options{
HostPort: hostPort,
Namespace: namespace,
ConnectionOptions: client.ConnectionOptions{
TLS: &tls.Config{Certificates: []tls.Certificate{cert}},
},
})
if err != nil {
log.Fatalln("Unable to connect to Temporal Cloud.", err)
}
defer temporalClient.Close()
// Create a new Worker
yourWorker := worker.New(temporalClient, "backgroundcheck-boilerplate-task-queue-cloud", worker.Options{})
// Register Workflows
yourWorker.RegisterWorkflow(workflows.BackgroundCheck)
// Register Activities
yourWorker.RegisterActivity(activities.SSNTraceActivity)
// Start the Worker Process
err = yourWorker.Run(worker.InterruptCh())
if err != nil {
log.Fatalln("Unable to start the Worker Process", err)
}
}

To run a Temporal Cloud Worker, you'll change some parameters in your Client connection code, such as updating the namespace and gRPC endpoint. You'll use:

Copy the Namespace Id and the gRPC endpoint from the Namespace detail Web page on Temporal Cloud Namespaces. Click on a Namespace name to open the Namespace details.

Run a Self-hosted Worker

To deploy a self-hosted Worker to your Docker environment, you need to configure your Worker with the appropriate IP address and port.

Confirm network

The default docker-compose.yml file in the temporalio/docker-compose repo has the Temporal Server exposed on port 7233 on the temporal-network.

services:
# ...
temporal:
container_name: temporal
# ...
networks:
- temporal-network
ports:
- 7233:7233
# ...
# ...

If you are using a different or customized docker compose file, you can see the available networks by using the following command:

docker network ls

Confirm IP address

Get the IP address of the Docker network that the containers are using.

To do that, first inspect the network:

docker network inspect temporal-network

Look for the container named temporal.

Example output:

[
{
"Name": "temporal-network",
// ...
"Containers": {
// ...
"53cf62f0cc6cfd2a9627a2b5a4c9f48ffe5a858f0ef7b2eaa51bf7ea8fd0e86f": {
"Name": "temporal",
// ...
"IPv4Address": "172.18.0.4/16"
// ...
}
// ...
}
// ...
}
]

Copy the IP address part.

Customize Client options

Set IP address, port, and Namespace in the Temporal Client options.

View the source code in the context of the rest of the application code.

package main

import (
"log"

"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"

"documentation-samples-go/dev-guide/chapters/setup/activities"
"documentation-samples-go/dev-guide/chapters/setup/workflows"
)


func main() {
// Initialize a Temporal Client
// Specify the IP, port, and Namespace in the Client options
clientOptions := client.Options{
HostPort: "172.18.0.4:7233",
Namespace: "backgroundcheck_namespace",
}
temporalClient, err := client.Dial(clientOptions)
if err != nil {
log.Fatalln("Unable to create a Temporal Client", err)
}
defer temporalClient.Close()
// Create a new Worker
yourWorker := worker.New(temporalClient, "backgroundcheck-boilerplate-task-queue-self-hosted", worker.Options{})
// Register Workflows
yourWorker.RegisterWorkflow(workflows.BackgroundCheck)
// Register Activities
yourWorker.RegisterActivity(activities.SSNTraceActivity)
// Start the Worker Process
err = yourWorker.Run(worker.InterruptCh())
if err != nil {
log.Fatalln("Unable to start the Worker Process", err)
}
}

Build and deploy Docker image

Add a Docker file to the root of your Background Check application project.

Name the file dockerfile, with no extensions, and add the following configuration:

FROM golang:1.20 AS builder

WORKDIR /app

COPY . .

RUN go get
RUN go build -o bin ./self_hosted/main_dacx.go

ENTRYPOINT ["/app/bin"]
info

Make sure the Golang builder version matches the one used by the Go SDK. Different versions of the Go SDK may use different versions of Golang.

Then build the Docker image using the following command:

docker build . -t backgroundcheck-worker-image:latest

Now run the Worker on the same network as the Temporal Cluster containers using the following command:

docker run --network temporal-network backgroundcheck-worker-image:latest

Start Workflow using the CLI

How to start a Workflow using the CLI

You can use the Temporal CLI to start a Workflow whether you are using a local development server, Temporal Cloud, or are in a self-hosted environment. However, you need to provide additional options to the command when operating with the Temporal Cloud or self-hosted environments.

Local dev Server

How to start a Workflow with the Temporal CLI while using the local development server

Use the Temporal CLI temporal workflow start command to start your Workflow.

temporal workflow start \
--task-queue backgroundcheck-boilerplate-task-queue-local \
--type BackgroundCheck \
--input '"555-55-5555"' \
--namespace backgroundcheck_namespace \
--workflow-id backgroundcheck_workflow

Parameters breakdown

  • --task-queue: The name of the Task Queue for all the Workflow Execution's Tasks. Unless otherwise specified, Activity Executions use the Workflow Execution's Task Queue name by default.
  • --type: This is the Workflow Type name. By default, this is the function name. In the Go SDK, this name can be customized when registering the Worklow with the Workflow.
  • --input: This must be a valid JSON object that can be unmarshaled into the parameter(s) that the Workflow function accepts. Read more about how the Temporal Platform handles your application data in the Data conversion guide.
  • --namespace: This is the Namespace that you want to run your Temporal Application in.
  • --workflow-id: A Workflow Id is a custom identifier provided by you. The Temporal Platform generates one if one isn't provided. However, we highly recommend supplying your own Workflow Id with your own naming convention. A Workflow Id Reuse Policy enables fine controls over whether Workflow Ids can be reused in the Platform within the Retention Period.

For more details, see the temporal workflow start command API reference.

After you start the Workflow, you can see it in the Temporal Platform. Use the Temporal CLI or the Temporal Web UI to monitor the Workflow's progress.

List Workflows

Use the 'temporal workflow list` command to list all of the Workflows in the Namespace:

temporal workflow list \
--namespace backgroundcheck_namespace

View in Web UI

You can also use the Web UI to see the Workflows associated with the Namespace.

The local development server starts the Web UI at http://localhost:8233.

When you visit for the first time, the Web UI directs you to http://localhost:8233/namespaces/default/workflows.

Use the Namespace dropdown to select the project Namespace you created earlier.

Web UI Namespace selection

Web UI Namespace selection

You should now be at http://localhost:8233/namespaces/backgroundcheck_namespace/workflows.

Confirm polling Worker

If you ever want to confirm that a Worker is polling on the Task Queue that the Workflow started on, you can visit the Workflow Execution's details page and click on the Task Queue name.

Click on the Task Queue name to view polling Workers

Click on the Task Queue name to view polling Workers

This will direct you to a page where you can view the Workers polling that Task Queue. If there are none, the application won't run.

Confirm Workers polling Task Queue

Confirm Workers polling Task Queue

Temporal Cloud

How to start a Workflow with Temporal CLI when using Temporal Cloud

Run the temporal workflow start command, and make sure to specify the certificate and private key arguments.

temporal workflow start \
--task-queue backgroundcheck-boilerplate-task-queue-cloud \
--type BackgroundCheck \
--input '"555-55-5555"' \
--namespace <namespace>.<account-id> \
--workflow-id backgroundcheck_workflow \
--address <namespace>.<account-id>.tmprl.cloud:<port> \
--tls-cert-path ca.pem \
--tls-key-path ca.key

Make sure that the certificate path, private key path, Namespace, and address argument values match your project.

Use environment variables

Use environment variables as a way to quickly switch between a local dev server and Temporal Cloud, for example.

You can customize the environment names to be anything you want.

# set Cloud env variables
temporal env set cloud.namespace <namespace>.<account-id>
temporal env set cloud.address <namespace>.<account-id>.tmprl.cloud:<port>
temporal env set cloud.tls-cert-path ca.pem
temporal env set cloud.tls-key-path ca.key
# set local env variables
temporal env set local.namespace <namespace>

In this way, you can just provide a single --env command option when using the CLI rather than specifying each connection option in every command.

temporal workflow start \
# ...
--env cloud \
# ...

List Workflows

Run the temporal workflow list command, and make sure to specify the certificate and private key arguments.

temporal workflow list \
--tls-cert-path ca.pem \
--tls-key-path ca.key \
--namespace <namespace>.<account-id> \
--address <namespace>.<account-id>.tmprl.cloud:<port>

View in Web UI

Visit the Workflows page of your Cloud Namespace. The URL will look something like the following:

https://cloud.temporal.io/namespaces/<namespace>.<account-id>/workflows

View Workflows in the Cloud UI

View Workflows in the Cloud UI

Self-hosted

How to start a Workflow with the Temporal CLI when using a Self-hosted Cluster

Use your Temporal CLI alias to run the temporal workflow start command and start your Workflow.

temporal_docker workflow start \
--task-queue backgroundcheck-boilerplate-task-queue-self-hosted \
--type BackgroundCheck \
--input '"555-55-5555"' \
--namespace backgroundcheck_namespace \
--workflow-id backgroundcheck_workflow

List Workflows

Using your Temporal CLI alias, run the temporal workflow list command. This command lists the Workflows Executions within the Namespace:

temporal_docker workflow list \
--namespace backgroundcheck_namespace

View in the Web UI

When you visit for the first time, the Web UI directs you to http://localhost:8233/namespaces/default/workflows.

Use the Namespace dropdown to select the project Namespace you created earlier.

You should now be at http://localhost:8080/namespaces/backgroundcheck_namespace/workflows.

Add a testing framework

How to add a Testing Framework and Tests for the Workflow and Activity.

Each Temporal SDK has a testing suite that can be used in conjunction with a typical language specific testing framework. In the Temporal Go SDK, the testsuite package (https://pkg.go.dev/go.temporal.io/sdk/testsuite) provides a test environment in which the Workflow and Activity code may be run for test purposes.

View the source code in the context of the rest of the application code.

package setup

import (
"testing"

"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"go.temporal.io/sdk/testsuite"

"documentation-samples-go/dev-guide/chapters/setup/activities"
"documentation-samples-go/dev-guide/chapters/setup/workflows"
)


// UnitTestSuite is a struct that wraps around the testing suites
type UnitTestSuite struct {
// Add testify test suite package
suite.Suite
// Add the Temporal Go SDK Workflow test suite
testsuite.WorkflowTestSuite
}
// ...
// Test_BackgroundCheckApplication runs the full set of tests in this application.
func Test_BackgroundCheckApplication(t *testing.T) {
s := &UnitTestSuite{}
suite.Run(t, s)
}

In this example, we use a custom struct that absorbs both the testing functionality from testify (https://pkg.go.dev/github.com/stretchr/testify/suite) via suite.Suite and the testing functionality from the Temporal test framework via testsuite.WorkflowTestSuite. Next we create a regular test function recognized by the go test command, and pass an instance of the struct to suite.Run.

Add Workflow function tests

We can test Workflow code for the following conditions:

  • Workflow status. For example, did the Workflow reach a completed status?
  • Workflow returned an error. Did the Workflow function return an error?
  • Error when checking for a result of a Workflow. Is there an error in getting the result returned by the Workflow?
  • Workflow return value. If the Workflow did return something other than an error, is it what you expected it to be?

We can also perform a Workflow Replay test, and we'll provide detailed coverage of this topic in another section.

View the source code in the context of the rest of the application code.


const ssn string = "555-55-5555"

// Test_BackgroundCheckWorkflow tests the BackgroundCheck Workflow function
func (s *UnitTestSuite) Test_BackgroundCheckWorkflow() {
// Initialize a Temporal Go SDK Workflow test environment.
// The best practice is to create a new environment for each Workflow test.
// Doing so ensures that each test runs in its own isolated sandbox.
env := s.NewTestWorkflowEnvironment()
// Mock the Activity Execution for the Workflow
ssnTraceResult := "pass"
env.OnActivity(activities.SSNTraceActivity, mock.Anything, ssn).Return(&ssnTraceResult, nil)
// Run the Workflow in the test environment
env.ExecuteWorkflow(workflows.BackgroundCheck, ssn)
// Check that the Workflow reach a completed status
s.True(env.IsWorkflowCompleted())
// Check whether the Workflow returned an error
s.NoError(env.GetWorkflowError())
// Check that no error is returned while getting the result
// And check for the expected value of the Workflow result
var result string
s.NoError(env.GetWorkflowResult(&result))
s.Equal(result, ssnTraceResult)
}

Calling env.ExecuteWorkflow(...) executes the Workflow logic and any invoked Activities inside the test process. The first parameter of env.ExecuteWorkflow(...) contains a reference to Workflow function and any parameters that the Workflow needs.

The call to env.OnActivity is important, because if this call is not made to "mock" the execution or another function is used to replace it, the test environment will execute the actual Activity code including any calls to outside services.

After executing the Workflow in the above example, we assert that the Workflow ran through completion via the call to env.IsWorkflowComplete(). We also assert that no errors were returned by asserting on the return value of env.GetWorkflowError().

If our Workflow returned a value, we could have retrieved that value via a call to s.env.GetWorkflowResult(&value) and had additional asserts on that value.

Add Activity function tests

We can test Activity code for the following conditions:

  • Error when invoking the Activity Execution.
  • Error when checking for the result of the Activity Execution.
  • Activity return values. Check to ensure the return value is expected.
View the source code in the context of the rest of the application code.


// Test_SSNTraceActivity tests the SSNTraceActivity function
func (s *UnitTestSuite) Test_SSNTraceActivity() {
// Create a test environment
env := s.NewTestActivityEnvironment()
// Register Activity with the enviroment
env.RegisterActivity(activities.SSNTraceActivity)
// Run the Activity in the test enviroment
future, err := env.ExecuteActivity(activities.SSNTraceActivity, ssn)
// Check there was no error on the call to execute the Activity
s.NoError(err)
// Check that there was no error returned from the Activity
var result string
s.NoError(future.Get(&result))
// Check for the expected return value.
s.Equal("pass", result)
}