Overview

2 Hello, Earth! Extend your hello world

This chapter turns the classic “hello world” into a small, testable Go program. It starts by creating a module and a minimal main that prints to standard output with fmt.Println, while explaining package main, imports, exported vs. unexported identifiers (capitalization), and sensible naming and formatting conventions. The broader goal is to move beyond a one-liner by writing code that is both documented and verifiable, and by embracing Go’s Unicode support to greet users across writing systems.

Testing is introduced as a core practice. The chapter first shows how to verify standard output with Example functions (for instance, ExampleMain using // Output:), then refactors printing into a separate greet function that returns a string so it can be covered by conventional unit tests with the testing package. It models the preparation–execution–assertion–teardown flow, uses the “want/got” idiom, and reports failures via t.Errorf. To add language variations, greet is extended to accept a language parameter (a custom string-based type) and selects messages with a clear switch; the tests evolve from per-scenario functions to a concise, table-driven style that iterates named cases with t.Run.

To scale cleanly, the switch is replaced with a phrasebook map from language codes to greetings, illustrating Go’s multiple-return lookups (value, ok) and the need to check ok to detect unsupported inputs. The program becomes a simple CLI by parsing a -lang flag with the flag package (StringVar and Parse), converting the result to the language type, and printing the appropriate greeting. By the end, you have a modular, extendable greeter with deterministic tests, practical patterns for maps and table-driven tests, and a foundation for growing features such as more languages and input validation.

Summary

  • go run allows you to quickly execute a Go program without creating a binary, making it useful during development.
  • Writing tests alongside your code, rather than after it, ensures the mental model of expected inputs and outputs is fresh and accurate, leading to better test coverage.
  • go test is used to execute tests written for your code, following Go's naming conventions for test files and functions.
  • The testing package provides everything you need to write and run tests, including support for assertions and benchmarks.
  • Table-Driven Tests are a Go best practice for testing functions across multiple inputs and outputs. By using slices of test cases, you can easily iterate through various scenarios in a structured way.
  • Example functions are special tests used to check the output of a function by verifying what is written to the standard output. These are often used to demonstrate usage.
  • Each test generally follows four phases: preparation, where you set up inputs; execution, where you run the code being tested; decision, where you compare the result with expected output; and teardown, where you clean up resources.
  • The flag package allows for parsing command-line arguments, making your Go applications more flexible and interactive.
  • Maps in Go are a powerful data structure for storing key-value pairs. Accessing a map returns both the value and a boolean indicating if the key was found.
  • Define custom types when they provide meaningful context over built-in types. For example, a UserID type can be more descriptive than an int.
  • Use if statements for simple binary conditions. For more complex cases, prefer switch statements or maps to handle multiple conditions more cleanly.

FAQ

How do I print “Hello world” in Go, and what are package main and func main for?Create a file named main.go, declare package main, import fmt, and call fmt.Println. The main package marks an executable program, and the main() function is the entry point that runs when you execute the program. Example: fmt.Println("Hello world") writes to standard output.
Why does Println start with a capital letter?In Go, identifiers that start with a capital letter are exported from their package. Println is exported by the fmt package so it can be used from other packages (like your main package). Lowercase identifiers are unexported (package-internal).
How can I test what a function prints to standard output?Use an Example function in a _test.go file. Name it following Example<Function>, call the function, then place a comment line starting with “// Output:” followed by the expected output on the next lines. During go test, Go captures stdout and compares it to the expected lines.
What’s the difference between Example and Test functions?Example functions serve as runnable documentation and (optionally) verify stdout when “// Output:” is present. Test functions use the signature TestXxx(t *testing.T), call code, and assert results (e.g., with t.Errorf). They’re better for checking return values and state rather than stdout.
How do I refactor Hello World into a reusable greet function and test it?Move the message creation into a greet function that returns a string, have main() print its result, and write a TestGreet(t *testing.T) that sets want := "Hello world", calls got := greet(), and compares got != want, failing with t.Errorf if they differ.
How do I support multiple languages in greet?Introduce a dedicated type, e.g., type language string, and update greet to accept a language. Start with a switch on the value (e.g., "en", "fr") and return the appropriate greeting. Provide a default for unknown languages (empty string or a helpful message).
Why switch to a map for greetings instead of a big switch?As languages grow, a switch becomes long and harder to read. A map[language]string (a hash table) keeps the data separate and scalable. Lookups return two values: the greeting and a boolean ok indicating if the key exists. Use ok to distinguish “missing” from an empty string and return a clear “unsupported language” message when not found.
What is a table-driven test and how do I write one for greet?Create a small struct (e.g., testCase { lang language; want string }), define a map or slice of named test cases, and loop over them with for range. For each case, run t.Run(name, func(t *testing.T) { got := greet(tc.lang); compare got to tc.want }). This avoids repetitive test code and makes adding scenarios simple.
How do I let users choose the language from the command line?Use the flag package. Declare a variable (var lang string), bind it with flag.StringVar(&lang, "lang", "en", "Language code, e.g. en, fr..."), call flag.Parse(), then pass language(lang) to greet. Alternatively, flag.String returns a pointer: p := flag.String("lang", "en", "..."); use *p after flag.Parse().
When should I use internal vs external tests in Go?Internal tests live in the same package (package name without _test) and can access unexported identifiers—use this when you need to test internals. External tests use package packagename_test and only access exported API—use this to verify public behavior as a user would. Both approaches complement each other.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Learn Go with Pocket-Sized Projects ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Learn Go with Pocket-Sized Projects ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Learn Go with Pocket-Sized Projects ebook for free