Test-driven basics in Go - Time & Space Complexity
When writing tests first, it is important to know how the time your tests take grows as your code runs on bigger inputs.
We want to see how the test and code execution time changes when input size changes.
Analyze the time complexity of the following test-driven code snippet.
func Sum(nums []int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func TestSum(t *testing.T) {
result := Sum([]int{1, 2, 3, 4})
if result != 10 {
t.Errorf("Expected 10, got %d", result)
}
}
This code defines a function that adds numbers in a list and a test that checks if it works correctly.
Look for loops or repeated steps in the code.
- Primary operation: Looping through each number in the input list to add it.
- How many times: Once for each number in the list.
As the list gets bigger, the time to add all numbers grows in a straight line.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 additions |
| 100 | 100 additions |
| 1000 | 1000 additions |
Pattern observation: Doubling the input doubles the work because each number is added once.
Time Complexity: O(n)
This means the time grows directly with the number of items you add.
[X] Wrong: "The test time stays the same no matter how many numbers I add."
[OK] Correct: The test runs the function, and the function adds each number, so more numbers mean more work and more time.
Understanding how test and code time grows helps you write better tests and code that work well even with bigger inputs.
"What if the Sum function used recursion instead of a loop? How would the time complexity change?"
Practice
testing package?Solution
Step 1: Understand the role of tests in Go
Tests are written to verify that the code behaves as expected.Step 2: Identify what the
Thetestingpackage doestestingpackage provides tools to write and run tests that check code correctness.Final Answer:
To check if the code works correctly by running test functions -> Option CQuick Check:
Tests check code correctness = B [OK]
- Thinking tests improve speed
- Confusing tests with comments
- Assuming tests create UI
Solution
Step 1: Recall Go test function naming rules
Test functions must start with "Test" and take a*testing.Tparameter.Step 2: Match the correct function signature
Onlyfunc TestMyFunc(t *testing.T) {}follows this rule exactly.Final Answer:
func TestMyFunc(t *testing.T) {} -> Option DQuick Check:
Test functions start with "Test" = C [OK]
- Starting function name with lowercase 'test'
- Adding extra words before 'Test'
- Using wrong parameter types
go test on this test file?package main
import "testing"
func Add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
if Add(2, 3) != 5 {
t.Error("Expected 5")
}
}Solution
Step 1: Analyze the Add function and test
The Add function returns the sum of 2 and 3, which is 5.Step 2: Check the test condition
The test expects Add(2, 3) to be 5, which is true, so no error is called.Final Answer:
PASS -> Option BQuick Check:
Correct sum means test passes = D [OK]
- Assuming test fails when condition is true
- Thinking test prints 'Expected 5' on success
- Confusing test failure with compile error
package main
import "testing"
func TestMultiply(t *testing.T) {
result := Multiply(2, 3)
if result != 6 {
t.Error("Expected 6")
}
}Solution
Step 1: Check if Multiply function is defined
The code snippet does not show any definition or import of Multiply function.Step 2: Understand Go requires functions to be defined or imported
Without definition or import, the code will not compile.Final Answer:
Multiply function is not defined -> Option AQuick Check:
Undefined function causes compile error = A [OK]
- Thinking test function names can be lowercase
- Confusing t.Error with t.Fail
- Assuming missing import causes runtime error
Solution
Step 1: Understand the test goal
The test should fail if the function returns an empty string "".Step 2: Check each option's condition
func TestEmpty(t *testing.T) { if MyFunc() == "" { t.Error("Result should not be empty") } } fails when MyFunc() returns "", which matches the goal.Final Answer:
func TestEmpty(t *testing.T) { if MyFunc() == "" { t.Error("Result should not be empty") } } -> Option AQuick Check:
Fail on empty string = A [OK]
- Checking for nil instead of empty string
- Checking for zero instead of string
- Reversing the condition logic
Ready to prove it?
Pass the test and this pattern moves unknown
