Tricky Interview Questions for Senior Golang Developers
Question 1: Understanding the go test Tool
Problem Statement:
What is the go test tool? How is it used to write and run tests in Go?
Solution:
The go test tool is a command-line tool that is used to write and run tests in Go. It automatically discovers and executes test functions in your Go packages.
Explanation:
To write a test in Go, you need to create a file with the _test.go suffix. This file should contain test functions that start with the Test prefix. The go test tool will automatically discover and execute these test functions.
Question 2: Implementing a Simple Mocking Framework with Interfaces
Problem Statement:
Implement a simple mocking framework in Go using interfaces.
Solution:
package main
import (
"fmt"
)
type MyInterface interface {
DoSomething(a int, b string) string
}
type MyImplementation struct{}
func (m *MyImplementation) DoSomething(a int, b string) string {
return fmt.Sprintf("Real implementation: %d %s", a, b)
}
type MockMyInterface struct {
DoSomethingFunc func(a int, b string) string
}
func (m *MockMyInterface) DoSomething(a int, b string) string {
return m.DoSomethingFunc(a, b)
}
func main() {
mock := &MockMyInterface{
DoSomethingFunc: func(a int, b string) string {
return "Mocked result"
},
}
var i MyInterface = mock
result := i.DoSomething(1, "hello")
fmt.Println("Result:", result)
}
Explanation:
This example demonstrates how to create a mock implementation of an interface. The MockMyInterface struct implements the MyInterface interface. The DoSomething method of the MockMyInterface struct calls the DoSomethingFunc function, which is a function that you can define to return a mocked result.
Question 3: Understanding the testing.T Type
Problem Statement:
What is the testing.T type in Go? How is it used to write tests?
Solution:
The testing.T type is a type that is used to write tests in Go. It provides methods for reporting errors, logging messages, and skipping tests.
Explanation:
The testing.T type has the following methods:
- Error: Reports an error and continues execution.
- Errorf: Reports an error with a formatted message and continues execution.
- Fatal: Reports an error and stops execution.
- Fatalf: Reports an error with a formatted message and stops execution.
- Log: Logs a message.
- Logf: Logs a message with a formatted message.
- Skip: Skips the test.
- Skipf: Skips the test with a formatted message.
Question 4: Implementing a Simple Benchmark
Problem Statement:
Implement a simple benchmark in Go using the testing package.
Solution:
package main
import (
"fmt"
"testing"
)
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
func BenchmarkFibonacci(b *testing.B) {
for i := 0; i < b.N; i++ {
fibonacci(20)
}
}
func ExampleFibonacci() {
fmt.Println(fibonacci(10))
// Output:
// 55
}
Explanation:
This example demonstrates how to write a benchmark using the testing package. The BenchmarkFibonacci function is a benchmark function that benchmarks the fibonacci function. The b.N field is the number of iterations to run the benchmark.
Question 5: Understanding the testing.B Type
Problem Statement:
What is the testing.B type in Go? How is it used to write benchmarks?
Solution:
The testing.B type is a type that is used to write benchmarks in Go. It provides methods for controlling the benchmark, such as setting the number of iterations to run and measuring the time it takes to run the benchmark.
Explanation:
The testing.B type has the following methods:
- N: The number of iterations to run the benchmark.
- ResetTimer: Resets the timer.
- StartTimer: Starts the timer.
- StopTimer: Stops the timer.
- SetBytes: Sets the number of bytes processed per iteration.
- ReportAllocs: Reports the number of allocations per iteration.
Question 6: Implementing a Simple Fuzz Test
Problem Statement:
Implement a simple fuzz test in Go using the testing/fuzz package.
Solution:
package main
import (
"fmt"
"strings"
"testing"
)
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < len(runes)/2; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func FuzzReverse(f *testing.F) {
testcases := []string{"hello", "world", " ", "敏感词", "!12345"}
for _, tc := range testcases {
f.Add(tc) // Use f.Add to provide a seed corpus
}
f.Fuzz(func(t *testing.T, orig string) {
rev := Reverse(orig)
doubleRev := Reverse(rev)
if orig != doubleRev {
t.Errorf("Before: %q, after: %q", orig, doubleRev)
}
if strings.Contains(orig, "\n") {
t.Errorf("String contains newline: %q", orig)
}
})
}
func main() {
fmt.Println(Reverse("hello"))
}
Explanation:
This example demonstrates how to write a fuzz test using the testing/fuzz package. The FuzzReverse function is a fuzz test function that fuzzes the Reverse function. The f.Fuzz method takes a function that takes a testing.T and a string as arguments. The fuzz test will generate random strings and pass them to the function. The function should check if the output of the Reverse function is correct.
Question 7: Understanding the testing/quick Package
Problem Statement:
What is the testing/quick package in Go? How is it used?
Solution:
The testing/quick package provides utilities for black box testing of functions. It is part of the testing package, which provides support for testing in Go.
Explanation:
The testing/quick package has the following functions:
- Check: Checks if a function satisfies a property for a large number of randomly generated inputs.
- CheckEqual: Checks if two functions are equivalent for a large number of randomly generated inputs.
Question 8: Implementing a Simple Property-Based Test
Problem Statement:
Implement a simple property-based test in Go using the testing/quick package.
Solution:
package main
import (
"fmt"
"strings"
"testing"
"testing/quick"
)
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < len(runes)/2; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func TestReverseInvertible(t *testing.T) {
f := func(s string) bool {
rev := Reverse(s)
doubleRev := Reverse(rev)
return s == doubleRev
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func main() {
fmt.Println(Reverse("hello"))
}
Explanation:
This example demonstrates how to write a property-based test using the testing/quick package. The TestReverseInvertible function is a test function that checks if the Reverse function is invertible. The quick.Check function takes a function that takes a string as an argument and returns a boolean. The quick.Check function will generate random strings and pass them to the function. The function should return true if the property holds for the input string.
Question 9: Understanding the go test -race Flag
Problem Statement:
What is the go test -race flag? How is it used to detect race conditions in Go code?
Solution:
The go test -race flag is a flag that is used to detect race conditions in Go code. It enables the race detector, which is a tool that can detect race conditions at runtime.
Explanation:
The race detector works by instrumenting your Go code to track the access to shared variables. When multiple goroutines access the same shared variable concurrently without proper synchronization, the race detector will report a race condition.
Question 10: Implementing a Simple Integration Test
Problem Statement:
Implement a simple integration test in Go.
Solution:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, world!")
}
func TestHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(handler)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
expected := "Hello, world!\n"
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Explanation:
This example demonstrates how to write a simple integration test. The TestHandler function creates a new HTTP request and a new HTTP response recorder. The function then calls the handler function with the request and response recorder. The function then checks if the response status code is correct and if the response body is correct.