You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/posts/2025-11-02-go-mod-breaks-tests.md
+22-35Lines changed: 22 additions & 35 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,37 +8,34 @@ categories:
8
8
- Golang
9
9
- Programming
10
10
---
11
-
12
11
## Introduction
13
12
If you’ve ever initialized a new Go project with `go mod init main` and later tried to write tests for it, you may have seen this cryptic error:
14
-
15
-
###
13
+
```
16
14
cannot import "main"
17
-
###
15
+
```
18
16
19
17
At first glance, this doesn’t make much sense — after all, your code and tests are both in `package main`. However, there’s a subtle interaction between how Go handles modules, packages, and test harnesses that causes this issue. Let’s walk through what’s actually happening.
20
18
21
19
---
22
-
23
20
## Setting Up the Problem
24
21
25
22
### The `go.mod` File
26
23
When you initialize a new Go module for an executable project, it’s common to do something like:
27
24
28
-
###
25
+
```Golang
29
26
go mod init main
30
-
###
27
+
```
31
28
32
29
That creates the following `go.mod` file:
33
30
34
-
###
31
+
```Golang
35
32
module main
36
33
go1.25.3
37
-
###
34
+
```
38
35
39
36
Then you might have a simple file like this:
40
37
41
-
###
38
+
```Golang
42
39
package main
43
40
44
41
funcmain() {
@@ -48,73 +45,66 @@ func main() {
48
45
funcsquare(nint) int {
49
46
return n * n
50
47
}
51
-
###
48
+
```
52
49
53
50
Now you decide to write a quick unit test in `main_test.go`:
54
51
55
-
###
52
+
```Golang
56
53
package main
57
54
58
55
import"testing"
59
56
60
57
funcTestSquare(t *testing.T) {
61
58
t.Log("Test successful")
62
59
}
63
-
###
60
+
```
64
61
65
62
At this point, everything looks fine — until you run:
66
63
67
-
###
64
+
```bash
68
65
go test
69
-
###
66
+
```
70
67
71
68
and get the following output:
72
69
73
-
###
70
+
```bash
74
71
# main.test
75
72
$WORK/b001/_testmain.go:14:8: could not import main (cannot import "main")
76
73
FAIL main [build failed]
77
74
FAIL
78
-
###
75
+
```
79
76
80
77
---
81
-
82
78
## Why This Happens
83
-
When you run `go test`, the Go toolchain generates a temporary test harness (a `_testmain.go` file). This harness imports the package under test so that it can invoke your test functions.
84
-
85
-
Here’s the catch: the test harness **must import the module** under its module path. When your module path is literally `"main"`, the compiler refuses — because `main` is reserved for executables and cannot be imported like a normal package.
86
-
87
-
This only affects `go test` (and `go test .`), because those commands rely on the generated import. When you explicitly specify files (e.g., `go test *.go`), Go skips module resolution and compiles everything together manually, avoiding the import step entirely.
79
+
When you run `go test`, the Go toolchain generates a temporary test harness (a `_testmain.go` file). This harness imports the package under test so that it can invoke your test functions. Here’s the catch: the test harness **must import the module** under its module path. When your module path is literally `"main"`, the compiler refuses — because `main` is reserved for executables and cannot be imported like a normal package. This only affects `go test` (and `go test .`), because those commands rely on the generated import. When you explicitly specify files (e.g., `go test *.go`), Go skips module resolution and compiles everything together manually, avoiding the import step entirely.
88
80
89
81
---
90
-
91
82
## The Fix
92
83
93
84
### Option 1: Rename the Module
94
85
Change your `go.mod` file to use a different name, for example:
95
86
96
-
###
87
+
```Golang
97
88
module myapp
98
89
go1.25.3
99
-
###
90
+
```
100
91
101
92
Then rerun:
102
93
103
-
###
94
+
```bash
104
95
go test
105
-
###
96
+
```
106
97
107
98
You should now see:
108
99
109
-
###
100
+
```bash
110
101
ok myapp 0.35s
111
-
###
102
+
```
112
103
113
104
### Option 2: Move Testable Code Out of `main`
114
105
In most cases, your `main` package should be minimal — it should only wire dependencies together. Any functions that need unit tests should live in a separate package (for example, `internal/mathutil`), which can then be imported both from your main application and from your test files.
115
106
116
107
---
117
-
118
108
## Key Takeaways
119
109
120
110
- The Go test harness always imports the package under test.
@@ -123,8 +113,5 @@ In most cases, your `main` package should be minimal — it should only wire dep
123
113
- Reserve `main` strictly for integration and startup logic.
124
114
125
115
---
126
-
127
116
## Conclusion
128
-
This issue isn’t a compiler bug — it’s just an edge case that surfaces when your module path collides with Go’s internal naming conventions.
129
-
130
-
If you ever see `cannot import "main"` when testing, remember: rename the module or refactor your code to follow Go’s standard package layout.
117
+
This issue isn’t a compiler bug — it’s just an edge case that surfaces when your module path collides with Go’s internal naming conventions. If you ever see `cannot import "main"` when testing, remember: rename the module or refactor your code to follow Go’s standard package layout.
0 commit comments