
Testing Command-Line Applications in Go: Best Practices
To effectively test command-line applications, you can leverage the os/exec package to simulate running commands and the testing package to structure your tests. This article will guide you through writing tests for a simple CLI application that performs basic arithmetic operations.
Example CLI Application
Let’s start with a simple CLI application that performs addition and subtraction. Here’s the code for our application:
// calculator.go
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
if len(os.Args) < 4 {
fmt.Println("Usage: calculator <add|subtract> <num1> <num2>")
return
}
operation := os.Args[1]
num1, err1 := strconv.Atoi(os.Args[2])
num2, err2 := strconv.Atoi(os.Args[3])
if err1 != nil || err2 != nil {
fmt.Println("Invalid numbers provided.")
return
}
switch operation {
case "add":
fmt.Println(num1 + num2)
case "subtract":
fmt.Println(num1 - num2)
default:
fmt.Println("Unknown operation. Use 'add' or 'subtract'.")
}
}Setting Up Tests
To test this application, we will create a test file named calculator_test.go. We will use the os/exec package to execute our CLI commands and capture their output.
Test Functions
Here’s how you can structure your tests:
// calculator_test.go
package main
import (
"bytes"
"os/exec"
"testing"
)
func runCalculator(args ...string) (string, error) {
cmd := exec.Command("go", "run", "calculator.go", args...)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
return out.String(), err
}
func TestAdd(t *testing.T) {
output, err := runCalculator("add", "3", "4")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
expected := "7\n"
if output != expected {
t.Errorf("Expected %q, got %q", expected, output)
}
}
func TestSubtract(t *testing.T) {
output, err := runCalculator("subtract", "10", "4")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
expected := "6\n"
if output != expected {
t.Errorf("Expected %q, got %q", expected, output)
}
}
func TestInvalidOperation(t *testing.T) {
output, err := runCalculator("multiply", "3", "4")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
expected := "Unknown operation. Use 'add' or 'subtract'.\n"
if output != expected {
t.Errorf("Expected %q, got %q", expected, output)
}
}
func TestInvalidNumbers(t *testing.T) {
output, err := runCalculator("add", "three", "four")
if err == nil {
t.Fatalf("Expected an error, got nil")
}
expected := "Invalid numbers provided.\n"
if output != expected {
t.Errorf("Expected %q, got %q", expected, output)
}
}Explanation of the Tests
- runCalculator Function: This helper function abstracts the command execution logic, allowing us to run our CLI application with different arguments and capture the output.
- TestAdd: This test checks if the addition operation produces the correct output.
- TestSubtract: This test verifies the subtraction operation.
- TestInvalidOperation: This test ensures that an unknown operation returns the appropriate error message.
- TestInvalidNumbers: This test checks the application’s response to invalid number inputs.
Running the Tests
To run your tests, simply execute the following command in your terminal:
go testThis command will automatically find and run all test functions in your calculator_test.go file.
Best Practices for Testing CLI Applications
| Best Practice | Description |
|---|---|
| Use Helper Functions | Create functions to encapsulate command execution to avoid code duplication. |
| Validate Output Thoroughly | Always check the output against expected results, including error messages. |
| Test Edge Cases | Consider how your application behaves with unexpected input or arguments. |
| Keep Tests Independent | Ensure that tests do not rely on the state left by other tests. |
| Use Descriptive Test Names | Name your tests clearly to indicate what functionality they are verifying. |
Conclusion
Testing command-line applications in Go requires a structured approach to ensure that all functionalities are verified. By leveraging the os/exec package to simulate command execution and capturing output, you can create comprehensive tests that validate the behavior of your CLI applications.
Learn more with useful resources:
