├── go.mod ├── goblin_logo.jpg ├── goblin_output.png ├── .gitignore ├── resolver_test.go ├── resolver.go ├── go.snippets ├── mono_reporter.go ├── .github └── workflows │ └── go.yml ├── LICENSE ├── race_test.go ├── describe_test.go ├── it_test.go ├── reporting.go ├── reporting_test.go ├── assertions.go ├── README.md ├── goblin.go ├── assertions_test.go └── goblin_test.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/franela/goblin 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /goblin_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franela/goblin/HEAD/goblin_logo.jpg -------------------------------------------------------------------------------- /goblin_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franela/goblin/HEAD/goblin_output.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /resolver_test.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestResolver(t *testing.T) { 8 | g := Goblin(t) 9 | 10 | g.Describe("Resolver", func() { 11 | g.It("Should resolve the stack until the test", func() { 12 | dummyFunc(g) 13 | }) 14 | }) 15 | } 16 | 17 | func dummyFunc(g *G) { 18 | stack := ResolveStack(5) 19 | g.Assert(len(stack)).Equal(6) 20 | } 21 | -------------------------------------------------------------------------------- /resolver.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "runtime/debug" 5 | "strings" 6 | ) 7 | 8 | func ResolveStack(skip int) []string { 9 | return cleanStack(debug.Stack(), skip) 10 | } 11 | 12 | func cleanStack(stack []byte, skip int) []string { 13 | arrayStack := strings.Split(string(stack), "\n") 14 | var finalStack []string 15 | for i := skip; i < len(arrayStack); i++ { 16 | if strings.Contains(arrayStack[i], ".go") { 17 | finalStack = append(finalStack, arrayStack[i]) 18 | } 19 | } 20 | return finalStack 21 | } 22 | -------------------------------------------------------------------------------- /go.snippets: -------------------------------------------------------------------------------- 1 | snippet gd 2 | g.Describe("${1:name}", func() { 3 | ${2} 4 | }) 5 | ${0} 6 | snippet git 7 | g.It("${1:name}", func() { 8 | ${2} 9 | }) 10 | ${0} 11 | snippet gait 12 | g.It("${1:name}", func(done Done) { 13 | done() 14 | ${2} 15 | }) 16 | ${0} 17 | snippet gb 18 | g.Before(func() { 19 | ${1} 20 | }) 21 | ${0} 22 | snippet gbe 23 | g.BeforeEach(func() { 24 | ${1} 25 | }) 26 | ${0} 27 | snippet ga 28 | g.After(func() { 29 | ${1} 30 | }) 31 | ${0} 32 | snippet gae 33 | g.AfterEach(func() { 34 | ${1} 35 | }) 36 | ${0} 37 | -------------------------------------------------------------------------------- /mono_reporter.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import () 4 | 5 | type Monochrome struct { 6 | } 7 | 8 | func (self *Monochrome) Red(text string) string { 9 | return "!" + text 10 | } 11 | 12 | func (self *Monochrome) Gray(text string) string { 13 | return text 14 | } 15 | 16 | func (self *Monochrome) Cyan(text string) string { 17 | return text 18 | } 19 | 20 | func (self *Monochrome) WithCheck(text string) string { 21 | return ">>>" + text 22 | } 23 | 24 | func (self *Monochrome) Green(text string) string { 25 | return text 26 | } 27 | 28 | func (self *Monochrome) Yellow(text string) string { 29 | return text 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | golang: [1.14, 1.16] 17 | steps: 18 | 19 | - name: Set up Go ${{ matrix.golang }} 20 | uses: actions/setup-go@v1 21 | with: 22 | go-version: ${{ matrix.golang }} 23 | id: go 24 | 25 | - name: Check out code into the Go module directory 26 | uses: actions/checkout@v2 27 | 28 | - name: Get dependencies 29 | run: | 30 | go get -v -t -d ./... 31 | 32 | - name: Tests 33 | run: go test -race -v . 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Marcos Lilljedahl and Jonathan Leibiusky 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /race_test.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestG_It_AsyncDone_Race(t *testing.T) { 10 | fakeTest := testing.T{} 11 | g := Goblin(&fakeTest) 12 | 13 | g.Describe("Async test", func() { 14 | g.It("Should not create a data race if done() is called multiple times", func(done Done) { 15 | go func() { 16 | time.Sleep(100 * time.Millisecond) 17 | done() 18 | done() 19 | }() 20 | }) 21 | }) 22 | } 23 | 24 | func TestG_It_Fail_Race(t *testing.T) { 25 | g := Goblin(new(testing.T)) 26 | 27 | g.Describe("Synchronous test", func() { 28 | g.It("Should not create a data race on fail", func() { 29 | g.Fail("Failed") 30 | }) 31 | }) 32 | } 33 | 34 | func TestG_It_Assert_Race(t *testing.T) { 35 | g := Goblin(new(testing.T)) 36 | g.SetReporter(Reporter(new(FakeReporter))) 37 | 38 | g.Describe("Should not create a data race", func() { 39 | g.It("Should fail", func() { 40 | g.Assert(0).Equal(1) 41 | }) 42 | g.It("Should pass", func() { 43 | }) 44 | }) 45 | } 46 | 47 | func TestG_Parallel_New_Goblin(t *testing.T) { 48 | wg := new(sync.WaitGroup) 49 | const cnt = 2 50 | 51 | wg.Add(cnt) 52 | for i := 0; i < cnt; i++ { 53 | go func() { 54 | Goblin(new(testing.T)) 55 | wg.Done() 56 | }() 57 | } 58 | 59 | wg.Wait() 60 | } 61 | -------------------------------------------------------------------------------- /describe_test.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestBefore(t *testing.T) { 8 | fakeTest := testing.T{} 9 | 10 | g := Goblin(&fakeTest) 11 | 12 | g.Describe("Numbers", func() { 13 | before := 0 14 | 15 | g.Before(func() { 16 | before++ 17 | }) 18 | 19 | g.It("Should have called before", func() { 20 | g.Assert(before).Equal(1) 21 | }) 22 | 23 | g.It("Should have called before only once", func() { 24 | g.Assert(before).Equal(1) 25 | }) 26 | }) 27 | 28 | if fakeTest.Failed() { 29 | t.Fatal("Failed") 30 | } 31 | } 32 | 33 | func TestMultipleBefore(t *testing.T) { 34 | fakeTest := testing.T{} 35 | 36 | g := Goblin(&fakeTest) 37 | 38 | g.Describe("Numbers", func() { 39 | before := 0 40 | 41 | g.Before(func() { 42 | before++ 43 | }) 44 | 45 | g.Before(func() { 46 | before++ 47 | }) 48 | 49 | g.It("Should have called all the registered before", func() { 50 | g.Assert(before).Equal(2) 51 | }) 52 | }) 53 | 54 | if fakeTest.Failed() { 55 | t.Fatal("Failed") 56 | } 57 | } 58 | 59 | func TestNestedBefore(t *testing.T) { 60 | fakeTest := testing.T{} 61 | 62 | g := Goblin(&fakeTest) 63 | 64 | g.Describe("Numbers", func() { 65 | before := 0 66 | 67 | g.Before(func() { 68 | before++ 69 | }) 70 | 71 | g.Describe("Addition", func() { 72 | g.Before(func() { 73 | before++ 74 | }) 75 | 76 | g.It("Should have called all the registered before", func() { 77 | g.Assert(before).Equal(2) 78 | }) 79 | 80 | g.It("Should have called all the registered before only once", func() { 81 | g.Assert(before).Equal(2) 82 | }) 83 | }) 84 | 85 | }) 86 | 87 | if fakeTest.Failed() { 88 | t.Fatal("Failed") 89 | } 90 | } 91 | 92 | func TestAfter(t *testing.T) { 93 | fakeTest := testing.T{} 94 | 95 | g := Goblin(&fakeTest) 96 | after := 0 97 | g.Describe("Numbers", func() { 98 | 99 | g.After(func() { 100 | after++ 101 | }) 102 | 103 | g.It("Should call after only once", func() { 104 | g.Assert(after).Equal(0) 105 | }) 106 | 107 | g.It("Should call after only once", func() { 108 | g.Assert(after).Equal(0) 109 | }) 110 | }) 111 | 112 | if fakeTest.Failed() || after != 1 { 113 | t.Fatal("Failed") 114 | } 115 | } 116 | 117 | func TestMultipleAfter(t *testing.T) { 118 | fakeTest := testing.T{} 119 | 120 | g := Goblin(&fakeTest) 121 | 122 | after := 0 123 | g.Describe("Numbers", func() { 124 | 125 | g.After(func() { 126 | after++ 127 | }) 128 | 129 | g.After(func() { 130 | after++ 131 | }) 132 | 133 | g.It("Should call all the registered after", func() { 134 | g.Assert(after).Equal(0) 135 | }) 136 | }) 137 | 138 | if fakeTest.Failed() && after != 2 { 139 | t.Fatal("Failed") 140 | } 141 | } 142 | 143 | func TestNestedAfter(t *testing.T) { 144 | fakeTest := testing.T{} 145 | 146 | g := Goblin(&fakeTest) 147 | after := 0 148 | g.Describe("Numbers", func() { 149 | 150 | g.After(func() { 151 | after++ 152 | }) 153 | 154 | g.Describe("Addition", func() { 155 | g.After(func() { 156 | after++ 157 | }) 158 | 159 | g.It("Should call all the registered after", func() { 160 | g.Assert(after).Equal(0) 161 | }) 162 | 163 | g.It("Should have called all the registered after only once", func() { 164 | g.Assert(after).Equal(0) 165 | }) 166 | }) 167 | 168 | }) 169 | 170 | if fakeTest.Failed() || after != 2 { 171 | t.Fatal("Failed") 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /it_test.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestBeforeEach(t *testing.T) { 8 | fakeTest := testing.T{} 9 | 10 | g := Goblin(&fakeTest) 11 | 12 | g.Describe("Numbers", func() { 13 | before := 0 14 | 15 | g.BeforeEach(func() { 16 | before++ 17 | }) 18 | 19 | g.It("Should have called beforeEach", func() { 20 | g.Assert(before).Equal(1) 21 | }) 22 | 23 | g.It("Should have called beforeEach also for this one", func() { 24 | g.Assert(before).Equal(2) 25 | }) 26 | }) 27 | 28 | if fakeTest.Failed() { 29 | t.Fatal("Failed") 30 | } 31 | } 32 | 33 | func TestMultipleBeforeEach(t *testing.T) { 34 | fakeTest := testing.T{} 35 | 36 | g := Goblin(&fakeTest) 37 | 38 | g.Describe("Numbers", func() { 39 | before := 0 40 | 41 | g.BeforeEach(func() { 42 | before++ 43 | }) 44 | 45 | g.BeforeEach(func() { 46 | before++ 47 | }) 48 | 49 | g.It("Should have called all the registered beforeEach", func() { 50 | g.Assert(before).Equal(2) 51 | }) 52 | }) 53 | 54 | if fakeTest.Failed() { 55 | t.Fatal("Failed") 56 | } 57 | } 58 | 59 | func TestNestedBeforeEach(t *testing.T) { 60 | fakeTest := testing.T{} 61 | 62 | g := Goblin(&fakeTest) 63 | 64 | g.Describe("Numbers", func() { 65 | before := 0 66 | 67 | g.BeforeEach(func() { 68 | before++ 69 | }) 70 | 71 | g.Describe("Addition", func() { 72 | g.BeforeEach(func() { 73 | before++ 74 | }) 75 | 76 | g.It("Should have called all the registered beforeEach", func() { 77 | g.Assert(before).Equal(2) 78 | }) 79 | 80 | g.It("Should have called all the registered beforeEach also for this one", func() { 81 | g.Assert(before).Equal(4) 82 | }) 83 | }) 84 | 85 | }) 86 | 87 | if fakeTest.Failed() { 88 | t.Fatal("Failed") 89 | } 90 | } 91 | 92 | func TestAfterEach(t *testing.T) { 93 | fakeTest := testing.T{} 94 | after := 0 95 | 96 | g := Goblin(&fakeTest) 97 | g.Describe("Numbers", func() { 98 | 99 | g.AfterEach(func() { 100 | after++ 101 | }) 102 | 103 | g.It("Should call afterEach after this test", func() { 104 | g.Assert(after).Equal(0) 105 | }) 106 | 107 | g.It("Should have called afterEach before this test", func() { 108 | g.Assert(after).Equal(1) 109 | }) 110 | }) 111 | 112 | if fakeTest.Failed() || after != 2 { 113 | t.Fatal("Failed") 114 | } 115 | } 116 | 117 | func TestMultipleAfterEach(t *testing.T) { 118 | fakeTest := testing.T{} 119 | 120 | g := Goblin(&fakeTest) 121 | 122 | after := 0 123 | g.Describe("Numbers", func() { 124 | 125 | g.AfterEach(func() { 126 | after++ 127 | }) 128 | 129 | g.AfterEach(func() { 130 | after++ 131 | }) 132 | 133 | g.It("Should call all the registered afterEach", func() { 134 | g.Assert(after).Equal(0) 135 | }) 136 | }) 137 | 138 | if fakeTest.Failed() || after != 2 { 139 | t.Fatal("Failed") 140 | } 141 | } 142 | 143 | func TestNestedAfterEach(t *testing.T) { 144 | fakeTest := testing.T{} 145 | 146 | g := Goblin(&fakeTest) 147 | after := 0 148 | 149 | g.Describe("Numbers", func() { 150 | 151 | g.AfterEach(func() { 152 | after++ 153 | }) 154 | 155 | g.Describe("Addition", func() { 156 | g.AfterEach(func() { 157 | after++ 158 | }) 159 | 160 | g.It("Should call all the registered afterEach", func() { 161 | g.Assert(after).Equal(0) 162 | }) 163 | 164 | g.It("Should have called all the registered afterEach", func() { 165 | g.Assert(after).Equal(2) 166 | }) 167 | }) 168 | 169 | }) 170 | 171 | if fakeTest.Failed() || after != 4 { 172 | t.Fatal("Failed") 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /reporting.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | type Reporter interface { 12 | BeginDescribe(string) 13 | EndDescribe() 14 | Begin() 15 | End() 16 | Failure(*Failure) 17 | ItTook(time.Duration) 18 | ItFailed(string) 19 | ItPassed(string) 20 | ItIsPending(string) 21 | ItIsExcluded(string) 22 | } 23 | 24 | type TextFancier interface { 25 | Red(text string) string 26 | Gray(text string) string 27 | Cyan(text string) string 28 | Green(text string) string 29 | Yellow(text string) string 30 | WithCheck(text string) string 31 | } 32 | 33 | type DetailedReporter struct { 34 | level, failed, passed, pending, excluded int 35 | failures []*Failure 36 | executionTime, totalExecutionTime time.Duration 37 | executionTimeMu sync.RWMutex 38 | fancy TextFancier 39 | } 40 | 41 | func (r *DetailedReporter) SetTextFancier(f TextFancier) { 42 | r.fancy = f 43 | } 44 | 45 | type TerminalFancier struct { 46 | } 47 | 48 | func (self *TerminalFancier) Red(text string) string { 49 | return "\033[31m" + text + "\033[0m" 50 | } 51 | 52 | func (self *TerminalFancier) Gray(text string) string { 53 | return "\033[90m" + text + "\033[0m" 54 | } 55 | 56 | func (self *TerminalFancier) Cyan(text string) string { 57 | return "\033[36m" + text + "\033[0m" 58 | } 59 | 60 | func (self *TerminalFancier) Green(text string) string { 61 | return "\033[32m" + text + "\033[0m" 62 | } 63 | 64 | func (self *TerminalFancier) Yellow(text string) string { 65 | return "\033[33m" + text + "\033[0m" 66 | } 67 | 68 | func (self *TerminalFancier) WithCheck(text string) string { 69 | return "\033[32m\u2713\033[0m " + text 70 | } 71 | 72 | func (r *DetailedReporter) getSpace() string { 73 | return strings.Repeat(" ", (r.level+1)*2) 74 | } 75 | 76 | func (r *DetailedReporter) Failure(failure *Failure) { 77 | r.failures = append(r.failures, failure) 78 | } 79 | 80 | func (r *DetailedReporter) print(text string) { 81 | fmt.Printf("%v%v\n", r.getSpace(), text) 82 | } 83 | 84 | func (r *DetailedReporter) printWithCheck(text string) { 85 | fmt.Printf("%v%v\n", r.getSpace(), r.fancy.WithCheck(text)) 86 | } 87 | 88 | func (r *DetailedReporter) BeginDescribe(name string) { 89 | fmt.Println("") 90 | r.print(name) 91 | r.level++ 92 | } 93 | 94 | func (r *DetailedReporter) EndDescribe() { 95 | r.level-- 96 | } 97 | 98 | func (r *DetailedReporter) ItTook(duration time.Duration) { 99 | r.executionTimeMu.Lock() 100 | defer r.executionTimeMu.Unlock() 101 | r.executionTime = duration 102 | r.totalExecutionTime += duration 103 | } 104 | 105 | func (r *DetailedReporter) ItFailed(name string) { 106 | r.failed++ 107 | r.print(r.fancy.Red(strconv.Itoa(r.failed) + ") " + name)) 108 | } 109 | 110 | func (r *DetailedReporter) ItPassed(name string) { 111 | r.passed++ 112 | r.printWithCheck(r.fancy.Gray(name)) 113 | } 114 | 115 | func (r *DetailedReporter) ItIsPending(name string) { 116 | r.pending++ 117 | r.print(r.fancy.Cyan("- " + name)) 118 | } 119 | 120 | func (r *DetailedReporter) ItIsExcluded(name string) { 121 | r.excluded++ 122 | r.print(r.fancy.Yellow("- " + name)) 123 | } 124 | 125 | func (r *DetailedReporter) Begin() { 126 | } 127 | 128 | func (r *DetailedReporter) End() { 129 | comp := fmt.Sprintf("%d tests complete", r.passed) 130 | 131 | r.executionTimeMu.RLock() 132 | t := fmt.Sprintf("(%d ms)", r.totalExecutionTime/time.Millisecond) 133 | r.executionTimeMu.RUnlock() 134 | 135 | //fmt.Printf("\n\n \033[32m%d tests complete\033[0m \033[90m(%d ms)\033[0m\n", r.passed, r.totalExecutionTime/time.Millisecond) 136 | fmt.Printf("\n\n %v %v\n", r.fancy.Green(comp), r.fancy.Gray(t)) 137 | 138 | if r.pending > 0 { 139 | pend := fmt.Sprintf("%d test(s) pending", r.pending) 140 | fmt.Printf(" %v\n\n", r.fancy.Cyan(pend)) 141 | } 142 | 143 | if r.excluded > 0 { 144 | excl := fmt.Sprintf("%d test(s) excluded", r.excluded) 145 | fmt.Printf(" %v\n\n", r.fancy.Yellow(excl)) 146 | } 147 | 148 | if len(r.failures) > 0 { 149 | fmt.Printf("%s \n\n", r.fancy.Red(fmt.Sprintf(" %d tests failed:", len(r.failures)))) 150 | 151 | } 152 | 153 | for i, failure := range r.failures { 154 | fmt.Printf(" %d) %s:\n\n", i+1, failure.TestName) 155 | fmt.Printf(" %s\n", r.fancy.Red(failure.Message)) 156 | for _, stackItem := range failure.Stack { 157 | fmt.Printf(" %s\n", r.fancy.Gray(stackItem)) 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /reporting_test.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "reflect" 5 | "sync" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | type FakeReporter struct { 11 | describes []string 12 | fails []string 13 | passes []string 14 | pending []string 15 | excluded []string 16 | ends int 17 | failures int 18 | executionTime time.Duration 19 | totalExecutionTime time.Duration 20 | executionTimeMu sync.RWMutex 21 | beginFlag, endFlag bool 22 | } 23 | 24 | func (r *FakeReporter) BeginDescribe(name string) { 25 | r.describes = append(r.describes, name) 26 | } 27 | 28 | func (r *FakeReporter) EndDescribe() { 29 | r.ends++ 30 | } 31 | 32 | func (r *FakeReporter) Failure(failure *Failure) { 33 | r.failures++ 34 | } 35 | 36 | func (r *FakeReporter) ItFailed(name string) { 37 | r.fails = append(r.fails, name) 38 | } 39 | 40 | func (r *FakeReporter) ItPassed(name string) { 41 | r.passes = append(r.passes, name) 42 | } 43 | 44 | func (r *FakeReporter) ItIsPending(name string) { 45 | r.pending = append(r.pending, name) 46 | } 47 | 48 | func (r *FakeReporter) ItIsExcluded(name string) { 49 | r.excluded = append(r.excluded, name) 50 | } 51 | 52 | func (r *FakeReporter) ItTook(duration time.Duration) { 53 | r.executionTimeMu.Lock() 54 | defer r.executionTimeMu.Unlock() 55 | r.executionTime = duration 56 | r.totalExecutionTime += duration 57 | } 58 | 59 | func (r *FakeReporter) Begin() { 60 | r.beginFlag = true 61 | } 62 | 63 | func (r *FakeReporter) End() { 64 | r.endFlag = true 65 | } 66 | 67 | func TestReporting(t *testing.T) { 68 | fakeTest := &testing.T{} 69 | reporter := FakeReporter{} 70 | fakeReporter := Reporter(&reporter) 71 | 72 | g := Goblin(fakeTest) 73 | g.SetReporter(fakeReporter) 74 | 75 | g.Describe("One", func() { 76 | g.It("Foo", func() { 77 | g.Assert(0).Equal(1) 78 | }) 79 | g.Describe("Two", func() { 80 | g.It("Bar", func() { 81 | g.Assert(0).Equal(0) 82 | }) 83 | }) 84 | }) 85 | 86 | if !reflect.DeepEqual(reporter.describes, []string{"One", "Two"}) { 87 | t.FailNow() 88 | } 89 | if !reflect.DeepEqual(reporter.fails, []string{"Foo"}) { 90 | t.FailNow() 91 | } 92 | if !reflect.DeepEqual(reporter.passes, []string{"Bar"}) { 93 | t.FailNow() 94 | } 95 | if reporter.ends != 2 { 96 | t.FailNow() 97 | } 98 | 99 | if !reporter.beginFlag || !reporter.endFlag { 100 | t.FailNow() 101 | } 102 | } 103 | 104 | func TestReportingTime(t *testing.T) { 105 | fakeTest := &testing.T{} 106 | reporter := FakeReporter{} 107 | fakeReporter := Reporter(&reporter) 108 | 109 | g := Goblin(fakeTest) 110 | g.SetReporter(fakeReporter) 111 | 112 | g.Describe("One", func() { 113 | g.AfterEach(func() { 114 | testTime := int64(reporter.executionTime / time.Millisecond) 115 | if testTime < 5 || testTime > 6 { 116 | t.Fatalf("wrong execution time: %d", testTime) 117 | } 118 | }) 119 | g.It("Foo", func() { 120 | time.Sleep(5 * time.Millisecond) 121 | }) 122 | g.Describe("Two", func() { 123 | g.It("Bar", func() { 124 | time.Sleep(5 * time.Millisecond) 125 | }) 126 | }) 127 | }) 128 | 129 | testTime := int64(reporter.executionTime / time.Millisecond) 130 | reporter.executionTimeMu.RLock() 131 | defer reporter.executionTimeMu.RUnlock() 132 | if int64(reporter.totalExecutionTime/time.Millisecond) < 10 { 133 | t.Fatalf("wrong execution time: %d", testTime) 134 | } 135 | } 136 | 137 | func TestReportingPending(t *testing.T) { 138 | fakeTest := &testing.T{} 139 | reporter := FakeReporter{} 140 | fakeReporter := Reporter(&reporter) 141 | 142 | g := Goblin(fakeTest) 143 | g.SetReporter(fakeReporter) 144 | 145 | g.Describe("One", func() { 146 | g.It("One") 147 | g.Describe("Two", func() { 148 | g.It("Two") 149 | }) 150 | }) 151 | 152 | if !reflect.DeepEqual(reporter.pending, []string{"One", "Two"}) { 153 | t.FailNow() 154 | } 155 | } 156 | 157 | func TestReportingExcluded(t *testing.T) { 158 | fakeTest := &testing.T{} 159 | reporter := FakeReporter{} 160 | fakeReporter := Reporter(&reporter) 161 | 162 | g := Goblin(fakeTest) 163 | g.SetReporter(fakeReporter) 164 | 165 | g.Describe("One", func() { 166 | g.Xit("One", func() { 167 | g.Assert(1).Equal(1) 168 | }) 169 | g.Describe("Two", func() { 170 | g.Xit("Two", func() { 171 | g.Assert(2).Equal(2) 172 | }) 173 | }) 174 | }) 175 | 176 | if !reflect.DeepEqual(reporter.excluded, []string{"One", "Two"}) { 177 | t.FailNow() 178 | } 179 | } 180 | 181 | func TestReportingErrors(t *testing.T) { 182 | fakeTest := &testing.T{} 183 | reporter := FakeReporter{} 184 | fakeReporter := Reporter(&reporter) 185 | 186 | g := Goblin(fakeTest) 187 | g.SetReporter(fakeReporter) 188 | 189 | g.Describe("Numbers", func() { 190 | g.It("Should make reporting add two errors ", func() { 191 | g.Assert(0).Equal(1) 192 | g.Assert(0).Equal(1) 193 | }) 194 | }) 195 | 196 | if reporter.failures != 1 { 197 | t.FailNow() 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /assertions.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | // Assertion represents a fact stated about a source object. It contains the 10 | // source object and function to call 11 | type Assertion struct { 12 | src interface{} 13 | fail func(interface{}) 14 | } 15 | 16 | func objectsAreEqual(a, b interface{}) bool { 17 | if reflect.TypeOf(a) != reflect.TypeOf(b) { 18 | return false 19 | } 20 | 21 | if reflect.DeepEqual(a, b) { 22 | return true 23 | } 24 | 25 | if fmt.Sprintf("%#v", a) == fmt.Sprintf("%#v", b) { 26 | return true 27 | } 28 | 29 | return false 30 | } 31 | 32 | // Format series of messages provided to an assertion. Separate messages from 33 | // the preamble of assertion with a comma and concatenate messages using spaces. 34 | // Messages that are purely whitespace will be wrapped with square brackets, so 35 | // the developer can glean that something was actually reported in a message. 36 | func formatMessages(messages ...interface{}) string { 37 | // Concatenate messages together. 38 | var fm strings.Builder 39 | for _, message := range messages { 40 | fm.WriteString(" ") 41 | 42 | // Format message then wrap with square brackets if only 43 | // whitespace. 44 | m := fmt.Sprintf("%v", message) 45 | if strings.TrimSpace(m) == "" { 46 | m = fmt.Sprintf("[%s]", m) 47 | } 48 | fm.WriteString(m) 49 | } 50 | 51 | if fm.Len() == 0 { 52 | return "" 53 | } 54 | return "," + fm.String() 55 | } 56 | 57 | // Eql is a shorthand alias of Equal for convenience 58 | func (a *Assertion) Eql(dst interface{}, messages ...interface{}) { 59 | a.Equal(dst, messages) 60 | } 61 | 62 | // Equal takes a destination object and asserts that a source object and 63 | // destination object are equal to one another. It will fail the assertion and 64 | // print a corresponding message if the objects are not equivalent. 65 | func (a *Assertion) Equal(dst interface{}, messages ...interface{}) { 66 | if !objectsAreEqual(a.src, dst) { 67 | a.fail(fmt.Sprintf("%#v %s %#v%s", a.src, "does not equal", dst, 68 | formatMessages(messages...))) 69 | } 70 | } 71 | 72 | // IsTrue asserts that a source is equal to true. Optional messages can be 73 | // provided for inclusion in the displayed message if the assertion fails. It 74 | // will fail the assertion if the source does not resolve to true. 75 | func (a *Assertion) IsTrue(messages ...interface{}) { 76 | if !objectsAreEqual(a.src, true) { 77 | a.fail(fmt.Sprintf("%v %s%s", a.src, 78 | "expected false to be truthy", 79 | formatMessages(messages...))) 80 | } 81 | } 82 | 83 | // IsFalse asserts that a source is equal to false. Optional messages can be 84 | // provided for inclusion in the displayed message if the assertion fails. It 85 | // will fail the assertion if the source does not resolve to false. 86 | func (a *Assertion) IsFalse(messages ...interface{}) { 87 | if !objectsAreEqual(a.src, false) { 88 | a.fail(fmt.Sprintf("%v %s%s", a.src, 89 | "expected true to be falsey", 90 | formatMessages(messages...))) 91 | } 92 | } 93 | 94 | // isNil returns whether a.src is nil or not. 95 | func (a *Assertion) isNil() bool { 96 | if !objectsAreEqual(a.src, nil) { 97 | specialKinds := []reflect.Kind{ 98 | reflect.Slice, reflect.Chan, 99 | reflect.Map, reflect.Ptr, 100 | reflect.Interface, reflect.Func, 101 | } 102 | t := reflect.TypeOf(a.src).Kind() 103 | for _, kind := range specialKinds { 104 | if t == kind { 105 | return reflect.ValueOf(a.src).IsNil() 106 | } 107 | } 108 | return false 109 | } 110 | return true 111 | } 112 | 113 | // IsNil asserts that source is nil. 114 | func (a *Assertion) IsNil(messages ...interface{}) { 115 | if !a.isNil() { 116 | message := fmt.Sprintf("%v %s%v", a.src, "expected to be nil", formatMessages(messages...)) 117 | a.fail(message) 118 | } 119 | } 120 | 121 | // IsNotNil asserts that source is not nil. 122 | func (a *Assertion) IsNotNil(messages ...interface{}) { 123 | if a.isNil() { 124 | message := fmt.Sprintf("%v %s%v", a.src, "is nil", formatMessages(messages...)) 125 | a.fail(message) 126 | } 127 | } 128 | 129 | // IsZero asserts that source is a zero value for its respective type. 130 | // If it is a structure, for example, all of its fields must have their 131 | // respective zero value: "" for strings, 0 for int, etc. Slices, arrays 132 | // and maps are only considered zero if they are nil. To check if these 133 | // type of values are empty or not, use the len() from the data source 134 | // with IsZero(). Example: g.Assert(len(list)).IsZero(). 135 | func (a *Assertion) IsZero(messages ...interface{}) { 136 | valueOf := reflect.ValueOf(a.src) 137 | 138 | if !valueOf.IsZero() { 139 | message := fmt.Sprintf("%#v %s%v", a.src, "is not a zero value", formatMessages(messages...)) 140 | 141 | a.fail(message) 142 | } 143 | } 144 | 145 | // IsNotZero asserts the contrary of IsZero. 146 | func (a *Assertion) IsNotZero(messages ...interface{}) { 147 | valueOf := reflect.ValueOf(a.src) 148 | 149 | if valueOf.IsZero() { 150 | message := fmt.Sprintf("%#v %s%v", a.src, "is a zero value", formatMessages(messages...)) 151 | a.fail(message) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Goblin 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/franela/goblin.svg)](https://travis-ci.org/franela/goblin) 5 | [![Go Reportcard](https://goreportcard.com/badge/github.com/franela/goblin)](https://goreportcard.com/report/github.com/franela/goblin) 6 | [![GoDoc](https://godoc.org/github.com/franela/goblin?status.svg)](https://godoc.org/github.com/franela/goblin) 7 | [![License](https://img.shields.io/github/license/franela/goblin.svg)](https://github.com/franela/goblin/blob/master/LICENSE.md) 8 | [![Release](https://img.shields.io/github/release/franela/goblin.svg)](https://github.com/franela/goblin/releases/latest) 9 | 10 | 11 | A [Mocha](http://mochajs.org/) like BDD testing framework written in Go that requires no additional dependencies. Requires no extensive documentation nor complicated steps to get it running. 12 | 13 | ![](https://github.com/marcosnils/goblin/blob/master/goblin_logo.jpg?raw=true) 14 | 15 | Why Goblin? 16 | ----------- 17 | 18 | Inspired by the flexibility and simplicity of Node BDD and frustrated by the 19 | rigorousness of Go way of testing, we wanted to bring a new tool to 20 | write self-describing and comprehensive code. 21 | 22 | 23 | 24 | What do I get with it? 25 | ---------------------- 26 | 27 | - Run tests as usual with `go test` 28 | - Colorful reports and beautiful syntax 29 | - Preserve the exact same syntax and behaviour as Node's Mocha 30 | - Nest as many `Describe` and `It` blocks as you want 31 | - Use `Before`, `BeforeEach`, `After` and `AfterEach` for setup and teardown your tests 32 | - No need to remember confusing parameters in `Describe` and `It` blocks 33 | - Use a declarative and expressive language to write your tests 34 | - Plug different assertion libraries 35 | - [Gomega](https://github.com/onsi/gomega) (supported so far) 36 | - Skip your tests the same way as you would do in Mocha 37 | - Automatic terminal support for colored outputs 38 | - Two line setup is all you need to get up running 39 | 40 | 41 | 42 | How do I use it? 43 | ---------------- 44 | 45 | Since ```go test``` is not currently extensive, you will have to hook Goblin to it. You do that by 46 | adding a single test method in your test file. All your goblin tests will be implemented inside this function. 47 | 48 | ```go 49 | package foobar 50 | 51 | import ( 52 | "testing" 53 | . "github.com/franela/goblin" 54 | ) 55 | 56 | func Test(t *testing.T) { 57 | g := Goblin(t) 58 | g.Describe("Numbers", func() { 59 | // Passing Test 60 | g.It("Should add two numbers ", func() { 61 | g.Assert(1+1).Equal(2) 62 | }) 63 | // Failing Test 64 | g.It("Should match equal numbers", func() { 65 | g.Assert(2).Equal(4) 66 | }) 67 | // Pending Test 68 | g.It("Should substract two numbers") 69 | // Excluded Test 70 | g.Xit("Should add two numbers ", func() { 71 | g.Assert(3+1).Equal(4) 72 | }) 73 | }) 74 | } 75 | ``` 76 | 77 | Ouput will be something like: 78 | 79 | ![](https://github.com/marcosnils/goblin/blob/master/goblin_output.png?raw=true) 80 | 81 | Nice and easy, right? 82 | 83 | Can I do asynchronous tests? 84 | ---------------------------- 85 | 86 | Yes! Goblin will help you to test asynchronous things, like goroutines, etc. You just need to add a ```done``` parameter to the handler function of your ```It```. This handler function should be called when your test passes. 87 | 88 | ```go 89 | ... 90 | g.Describe("Numbers", func() { 91 | g.It("Should add two numbers asynchronously", func(done Done) { 92 | go func() { 93 | g.Assert(1+1).Equal(2) 94 | done() 95 | }() 96 | }) 97 | }) 98 | ... 99 | ``` 100 | 101 | Goblin will wait for the ```done``` call, a ```Fail``` call or any false assertion. 102 | 103 | How do I use it with Gomega? 104 | ---------------------------- 105 | 106 | Gomega is a nice assertion framework. But it doesn't provide a nice way to hook it to testing frameworks. It should just panic instead of requiring a fail function. There is an issue about that [here](https://github.com/onsi/gomega/issues/5). 107 | While this is being discussed and hopefully fixed, the way to use Gomega with Goblin is: 108 | 109 | ```go 110 | package foobar 111 | 112 | import ( 113 | "testing" 114 | goblin "github.com/franela/goblin" 115 | . "github.com/onsi/gomega" 116 | ) 117 | 118 | func Test(t *testing.T) { 119 | g := goblin.Goblin(t) 120 | 121 | //special hook for gomega 122 | RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) 123 | 124 | g.Describe("lala", func() { 125 | g.It("lslslslsls", func() { 126 | Expect(1).To(Equal(10)) 127 | }) 128 | }) 129 | } 130 | ``` 131 | 132 | 133 | FAQ 134 | ---- 135 | 136 | ### How do I run specific tests? 137 | 138 | If `-goblin.run=$REGES` is supplied to the `go test` command then only tests that match the supplied regex will run 139 | 140 | 141 | Contributing 142 | ----- 143 | 144 | We do have a couple of [issues](https://github.com/franela/goblin/issues) pending. Feel free to contribute and send us PRs (with tests please :smile:). 145 | 146 | Special Thanks 147 | ------------ 148 | 149 | Special thanks to [Leandro Reox](https://github.com/leandroreox) (Leitan) for the goblin logo. 150 | -------------------------------------------------------------------------------- /goblin.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "regexp" 7 | "runtime" 8 | "sync" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | type Done func(error ...interface{}) 14 | 15 | type Runnable interface { 16 | run(*G) bool 17 | } 18 | 19 | type Itable interface { 20 | run(*G) bool 21 | failed(string, []string) 22 | } 23 | 24 | func (g *G) Describe(name string, h func()) { 25 | d := &Describe{name: name, h: h, parent: g.parent} 26 | 27 | if d.parent != nil { 28 | d.parent.children = append(d.parent.children, Runnable(d)) 29 | } 30 | 31 | g.parent = d 32 | 33 | h() 34 | 35 | g.parent = d.parent 36 | 37 | if g.parent == nil && d.hasTests { 38 | g.reporter.Begin() 39 | if d.run(g) { 40 | g.t.Fail() 41 | } 42 | g.reporter.End() 43 | } 44 | } 45 | 46 | func (g *G) Timeout(time time.Duration) { 47 | g.timeout = time 48 | g.timer.Reset(time) 49 | } 50 | 51 | type Describe struct { 52 | name string 53 | h func() 54 | children []Runnable 55 | befores []func() 56 | afters []func() 57 | afterEach []func() 58 | beforeEach []func() 59 | justBeforeEach []func() 60 | hasTests bool 61 | parent *Describe 62 | } 63 | 64 | func (d *Describe) runBeforeEach() { 65 | if d.parent != nil { 66 | d.parent.runBeforeEach() 67 | } 68 | 69 | for _, b := range d.beforeEach { 70 | b() 71 | } 72 | } 73 | 74 | func (d *Describe) runJustBeforeEach() { 75 | if d.parent != nil { 76 | d.parent.runJustBeforeEach() 77 | } 78 | 79 | for _, b := range d.justBeforeEach { 80 | b() 81 | } 82 | } 83 | 84 | func (d *Describe) runAfterEach() { 85 | 86 | for _, a := range d.afterEach { 87 | a() 88 | } 89 | 90 | if d.parent != nil { 91 | d.parent.runAfterEach() 92 | } 93 | } 94 | 95 | func (d *Describe) run(g *G) bool { 96 | failed := false 97 | if d.hasTests { 98 | g.reporter.BeginDescribe(d.name) 99 | 100 | for _, b := range d.befores { 101 | b() 102 | } 103 | 104 | for _, r := range d.children { 105 | if r.run(g) { 106 | failed = true 107 | } 108 | } 109 | 110 | for _, a := range d.afters { 111 | a() 112 | } 113 | 114 | g.reporter.EndDescribe() 115 | } 116 | 117 | return failed 118 | } 119 | 120 | type Failure struct { 121 | Stack []string 122 | TestName string 123 | Message string 124 | } 125 | 126 | type It struct { 127 | h interface{} 128 | name string 129 | parent *Describe 130 | failure *Failure 131 | failureMu sync.RWMutex 132 | reporter Reporter 133 | isAsync bool 134 | } 135 | 136 | func (it *It) run(g *G) bool { 137 | g.currentIt = it 138 | 139 | if it.h == nil { 140 | g.reporter.ItIsPending(it.name) 141 | return false 142 | } 143 | 144 | runIt(g, it) 145 | 146 | failed := false 147 | it.failureMu.RLock() 148 | if it.failure != nil { 149 | failed = true 150 | } 151 | it.failureMu.RUnlock() 152 | 153 | if failed { 154 | g.reporter.ItFailed(it.name) 155 | g.reporter.Failure(it.failure) 156 | } else { 157 | g.reporter.ItPassed(it.name) 158 | } 159 | return failed 160 | } 161 | 162 | func (it *It) failed(msg string, stack []string) { 163 | it.failureMu.Lock() 164 | defer it.failureMu.Unlock() 165 | it.failure = &Failure{Stack: stack, Message: msg, TestName: it.parent.name + " " + it.name} 166 | } 167 | 168 | type Xit struct { 169 | h interface{} 170 | name string 171 | parent *Describe 172 | failure *Failure 173 | reporter Reporter 174 | isAsync bool 175 | } 176 | 177 | func (xit *Xit) run(g *G) bool { 178 | g.currentIt = xit 179 | 180 | g.reporter.ItIsExcluded(xit.name) 181 | return false 182 | } 183 | 184 | func (xit *Xit) failed(msg string, stack []string) { 185 | xit.failure = nil 186 | } 187 | 188 | func parseFlags() { 189 | //Flag parsing 190 | flag.Parse() 191 | if *regexParam != "" { 192 | runRegex = regexp.MustCompile(*regexParam) 193 | } else { 194 | runRegex = nil 195 | } 196 | } 197 | 198 | var doParseOnce sync.Once 199 | var timeout = flag.Duration("goblin.timeout", 5*time.Second, "Sets default timeouts for all tests") 200 | var isTty = flag.Bool("goblin.tty", true, "Sets the default output format (color / monochrome)") 201 | var regexParam = flag.String("goblin.run", "", "Runs only tests which match the supplied regex") 202 | var runRegex *regexp.Regexp 203 | 204 | func Goblin(t *testing.T, arguments ...string) *G { 205 | doParseOnce.Do(func() { 206 | parseFlags() 207 | }) 208 | 209 | g := &G{t: t, timeout: *timeout} 210 | var fancy TextFancier 211 | if *isTty { 212 | fancy = &TerminalFancier{} 213 | } else { 214 | fancy = &Monochrome{} 215 | } 216 | 217 | g.reporter = Reporter(&DetailedReporter{fancy: fancy}) 218 | return g 219 | } 220 | 221 | func runIt(g *G, it *It) { 222 | g.mutex.Lock() 223 | g.timedOut = false 224 | g.mutex.Unlock() 225 | g.timer = time.NewTimer(g.timeout) 226 | g.shouldContinue = make(chan bool) 227 | if call, ok := it.h.(func()); ok { 228 | // the test is synchronous 229 | go func(c chan bool) { 230 | it.parent.runBeforeEach() 231 | it.parent.runJustBeforeEach() 232 | timeTrack(g, func() { call() }) 233 | it.parent.runAfterEach() 234 | c <- true 235 | }(g.shouldContinue) 236 | } else if call, ok := it.h.(func(Done)); ok { 237 | doneCalled := 0 238 | go func(c chan bool) { 239 | it.parent.runBeforeEach() 240 | it.parent.runJustBeforeEach() 241 | timeTrack(g, func() { 242 | call(func(msg ...interface{}) { 243 | if len(msg) > 0 { 244 | g.Fail(msg) 245 | } else { 246 | doneCalled++ 247 | if doneCalled > 1 { 248 | g.Fail("Done called multiple times") 249 | } 250 | it.parent.runAfterEach() 251 | c <- true 252 | } 253 | }) 254 | }) 255 | }(g.shouldContinue) 256 | } else { 257 | panic("Not implemented.") 258 | } 259 | select { 260 | case <-g.shouldContinue: 261 | case <-g.timer.C: 262 | //Set to nil as it shouldn't continue 263 | g.shouldContinue = nil 264 | g.timedOut = true 265 | g.Fail("Test exceeded " + fmt.Sprintf("%s", g.timeout)) 266 | } 267 | // Reset timeout value 268 | g.timeout = *timeout 269 | } 270 | 271 | type G struct { 272 | t *testing.T 273 | parent *Describe 274 | currentIt Itable 275 | timeout time.Duration 276 | reporter Reporter 277 | timedOut bool 278 | shouldContinue chan bool 279 | mutex sync.Mutex 280 | timer *time.Timer 281 | } 282 | 283 | func (g *G) SetReporter(r Reporter) { 284 | g.reporter = r 285 | } 286 | 287 | func (g *G) It(name string, h ...interface{}) { 288 | if matchesRegex(name) { 289 | it := &It{name: name, parent: g.parent, reporter: g.reporter} 290 | if g.parent == nil { 291 | panic(fmt.Sprintf("It(\"%s\") block should be written inside Describe() block.", name)) 292 | } 293 | notifyParents(g.parent) 294 | if len(h) > 0 { 295 | it.h = h[0] 296 | } 297 | g.parent.children = append(g.parent.children, Runnable(it)) 298 | } 299 | } 300 | 301 | func (g *G) Xit(name string, h ...interface{}) { 302 | if matchesRegex(name) { 303 | xit := &Xit{name: name, parent: g.parent, reporter: g.reporter} 304 | notifyParents(g.parent) 305 | if len(h) > 0 { 306 | xit.h = h[0] 307 | } 308 | g.parent.children = append(g.parent.children, Runnable(xit)) 309 | } 310 | } 311 | 312 | func matchesRegex(value string) bool { 313 | if runRegex != nil { 314 | return runRegex.MatchString(value) 315 | } 316 | return true 317 | } 318 | 319 | func notifyParents(d *Describe) { 320 | d.hasTests = true 321 | if d.parent != nil { 322 | notifyParents(d.parent) 323 | } 324 | } 325 | 326 | func (g *G) Before(h func()) { 327 | g.parent.befores = append(g.parent.befores, h) 328 | } 329 | 330 | func (g *G) BeforeEach(h func()) { 331 | g.parent.beforeEach = append(g.parent.beforeEach, h) 332 | } 333 | 334 | func (g *G) JustBeforeEach(h func()) { 335 | g.parent.justBeforeEach = append(g.parent.justBeforeEach, h) 336 | } 337 | 338 | func (g *G) After(h func()) { 339 | g.parent.afters = append(g.parent.afters, h) 340 | } 341 | 342 | func (g *G) AfterEach(h func()) { 343 | g.parent.afterEach = append(g.parent.afterEach, h) 344 | } 345 | 346 | func (g *G) Assert(src interface{}) *Assertion { 347 | return &Assertion{src: src, fail: g.Fail} 348 | } 349 | 350 | func timeTrack(g *G, call func()) { 351 | t := time.Now() 352 | defer func() { 353 | g.reporter.ItTook(time.Since(t)) 354 | }() 355 | call() 356 | } 357 | 358 | func (g *G) errorCommon(msg string, fatal bool) { 359 | if g.currentIt == nil { 360 | panic("Asserts should be written inside an It() block.") 361 | } 362 | g.currentIt.failed(msg, ResolveStack(9)) 363 | if g.shouldContinue != nil { 364 | g.shouldContinue <- true 365 | } 366 | 367 | if fatal { 368 | g.mutex.Lock() 369 | defer g.mutex.Unlock() 370 | if !g.timedOut { 371 | //Stop test function execution 372 | runtime.Goexit() 373 | } 374 | } 375 | } 376 | 377 | func (g *G) Fail(error interface{}) { 378 | message := fmt.Sprintf("%v", error) 379 | g.errorCommon(message, true) 380 | } 381 | 382 | func (g *G) FailNow() { 383 | g.t.FailNow() 384 | } 385 | 386 | func (g *G) Failf(format string, args ...interface{}) { 387 | message := fmt.Sprintf(format, args...) 388 | g.errorCommon(message, true) 389 | } 390 | 391 | func (g *G) Fatalf(format string, args ...interface{}) { 392 | message := fmt.Sprintf(format, args...) 393 | g.errorCommon(message, true) 394 | } 395 | 396 | func (g *G) Errorf(format string, args ...interface{}) { 397 | message := fmt.Sprintf(format, args...) 398 | g.errorCommon(message, false) 399 | } 400 | 401 | func (g *G) Helper() { 402 | g.t.Helper() 403 | } 404 | -------------------------------------------------------------------------------- /assertions_test.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | // So we can test asserting type against its type alias 10 | type String string 11 | 12 | // Helper for testing Assertion conditions 13 | type AssertionVerifier struct { 14 | ShouldPass bool 15 | didFail bool 16 | msg interface{} 17 | } 18 | 19 | func (a *AssertionVerifier) FailFunc(msg interface{}) { 20 | a.didFail = true 21 | a.msg = msg 22 | } 23 | 24 | func (a *AssertionVerifier) Verify(t *testing.T) { 25 | if a.didFail == a.ShouldPass { 26 | t.FailNow() 27 | } 28 | } 29 | 30 | func (a *AssertionVerifier) VerifyMessage(t *testing.T, message string) { 31 | a.Verify(t) 32 | if a.msg.(string) != message { 33 | t.Fatalf(`"%s" != "%s"`, a.msg, message) 34 | } 35 | } 36 | 37 | // Verify that a slice of messages is formatted as expected. 38 | func verifyFormat(t *testing.T, expected string, messages ...interface{}) { 39 | // Prepend the expected comma if there is at least one message. 40 | if len(messages) != 0 { 41 | expected = ", " + expected 42 | } 43 | 44 | message := formatMessages(messages...) 45 | if message != expected { 46 | t.Fatalf(`Message format: "%s" != "%s"`, message, expected) 47 | } 48 | } 49 | 50 | // Test that messages for assertions are formatted as expected. 51 | func TestFormatMessages(t *testing.T) { 52 | message := "foo bar" 53 | 54 | // No message. 55 | verifyFormat(t, "") 56 | 57 | // Single message. 58 | verifyFormat(t, "[]", "") 59 | verifyFormat(t, "[ ]", " ") 60 | verifyFormat(t, "", nil) 61 | verifyFormat(t, message, message) 62 | verifyFormat(t, message, fmt.Errorf("%s", message)) 63 | num := 12345 64 | verifyFormat(t, strconv.Itoa(num), num) 65 | 66 | // Multiple messages. 67 | verifyFormat(t, "[] [ ]", "", " ") 68 | verifyFormat(t, message+" "+message, message, message) 69 | } 70 | 71 | func TestEqual(t *testing.T) { 72 | 73 | verifier := AssertionVerifier{ShouldPass: true} 74 | a := Assertion{src: 1, fail: verifier.FailFunc} 75 | a.Equal(1) 76 | verifier.Verify(t) 77 | a.Eql(1) 78 | verifier.Verify(t) 79 | 80 | a = Assertion{src: "foo"} 81 | a.Equal("foo") 82 | verifier.Verify(t) 83 | a.Eql("foo") 84 | verifier.Verify(t) 85 | 86 | a = Assertion{src: map[string]string{"foo": "bar"}} 87 | a.Equal(map[string]string{"foo": "bar"}) 88 | verifier.Verify(t) 89 | a.Eql(map[string]string{"foo": "bar"}) 90 | verifier.Verify(t) 91 | 92 | verifier = AssertionVerifier{ShouldPass: false} 93 | a = Assertion{src: String("baz"), fail: verifier.FailFunc} 94 | a.Equal("baz") 95 | verifier.Verify(t) 96 | a.Eql("baz") 97 | verifier.Verify(t) 98 | } 99 | 100 | // Test Equal() outputs the correct message upon failure. 101 | func TestEqualWithMessage(t *testing.T) { 102 | verifier := AssertionVerifier{ShouldPass: false} 103 | a := Assertion{src: 1, fail: verifier.FailFunc} 104 | msg := "foo is not bar" 105 | a.Equal(0, msg) 106 | verifier.VerifyMessage(t, "1 does not equal 0, "+msg) 107 | } 108 | 109 | func TestIsTrue(t *testing.T) { 110 | verifier := AssertionVerifier{ShouldPass: true} 111 | a := Assertion{src: true, fail: verifier.FailFunc} 112 | a.IsTrue() 113 | verifier.Verify(t) 114 | 115 | verifier = AssertionVerifier{ShouldPass: false} 116 | a = Assertion{src: false, fail: verifier.FailFunc} 117 | a.IsTrue() 118 | verifier.Verify(t) 119 | } 120 | 121 | func TestIsFalse(t *testing.T) { 122 | verifier := AssertionVerifier{ShouldPass: true} 123 | a := Assertion{src: false, fail: verifier.FailFunc} 124 | a.IsFalse() 125 | verifier.Verify(t) 126 | 127 | verifier = AssertionVerifier{ShouldPass: false} 128 | a = Assertion{src: true, fail: verifier.FailFunc} 129 | a.IsFalse() 130 | verifier.Verify(t) 131 | } 132 | 133 | func TestIsFalseWithMessage(t *testing.T) { 134 | verifier := AssertionVerifier{ShouldPass: false} 135 | a := Assertion{src: true, fail: verifier.FailFunc} 136 | a.IsFalse("false is not true") 137 | verifier.Verify(t) 138 | verifier.VerifyMessage(t, "true expected true to be falsey, false is not true") 139 | } 140 | 141 | func TestIsTrueWithMessage(t *testing.T) { 142 | verifier := AssertionVerifier{ShouldPass: false} 143 | a := Assertion{src: false, fail: verifier.FailFunc} 144 | a.IsTrue("true is not false") 145 | verifier.Verify(t) 146 | verifier.VerifyMessage(t, "false expected false to be truthy, true is not false") 147 | } 148 | 149 | func TestIsNil(t *testing.T) { 150 | check := func(isNil interface{}, isNotNil interface{}) { 151 | verifier := AssertionVerifier{ShouldPass: true} 152 | a := Assertion{src: isNil, fail: verifier.FailFunc} 153 | a.IsNil() 154 | verifier.Verify(t) 155 | 156 | verifier = AssertionVerifier{ShouldPass: false} 157 | a = Assertion{src: isNotNil, fail: verifier.FailFunc} 158 | a.IsNil() 159 | verifier.Verify(t) 160 | } 161 | 162 | check(nil, struct {}{}) 163 | 164 | var s []struct{} 165 | check(s, make([]struct{}, 0)) 166 | 167 | var c chan struct{} 168 | check(c, make(chan struct{}, 0)) 169 | 170 | var m map[struct{}]struct{} 171 | check(m, make(map[struct{}]struct{}, 0)) 172 | 173 | var p *struct{} 174 | check(p, &s) 175 | 176 | var ni interface{} = nil 177 | var i interface{} = struct {}{} 178 | check(ni, i) 179 | 180 | var f func() 181 | check(f, check) 182 | } 183 | 184 | func TestIsNilWithMessage(t *testing.T) { 185 | verifier := AssertionVerifier{ShouldPass: false} 186 | a := Assertion{src: struct{}{}, fail: verifier.FailFunc} 187 | a.IsNil("value is not nil") 188 | verifier.Verify(t) 189 | verifier.VerifyMessage(t, "{} expected to be nil, value is not nil") 190 | } 191 | 192 | func TestIsNotNil(t *testing.T) { 193 | check := func(isNil interface{}, isNotNil interface{}) { 194 | verifier := AssertionVerifier{ShouldPass: false} 195 | a := Assertion{src: isNil, fail: verifier.FailFunc} 196 | a.IsNotNil() 197 | verifier.Verify(t) 198 | 199 | verifier = AssertionVerifier{ShouldPass: true} 200 | a = Assertion{src: isNotNil, fail: verifier.FailFunc} 201 | a.IsNotNil() 202 | verifier.Verify(t) 203 | } 204 | 205 | check(nil, struct {}{}) 206 | 207 | var s []struct{} 208 | check(s, make([]struct{}, 0)) 209 | 210 | var c chan struct{} 211 | check(c, make(chan struct{}, 0)) 212 | 213 | var m map[struct{}]struct{} 214 | check(m, make(map[struct{}]struct{}, 0)) 215 | 216 | var p *struct{} 217 | check(p, &s) 218 | 219 | var ni interface{} = nil 220 | var i interface{} = struct {}{} 221 | check(ni, i) 222 | 223 | var f func() 224 | check(f, check) 225 | } 226 | 227 | func TestIsNotNilWithMessage(t *testing.T) { 228 | verifier := AssertionVerifier{ShouldPass: false} 229 | a := Assertion{src: nil, fail: verifier.FailFunc} 230 | a.IsNotNil("value should not be nil") 231 | verifier.Verify(t) 232 | verifier.VerifyMessage(t, " is nil, value should not be nil") 233 | } 234 | 235 | func TestIsZeroForStructs(t *testing.T) { 236 | source := struct{ Name string }{} 237 | verifier := AssertionVerifier{ShouldPass: true} 238 | a := Assertion{src: source, fail: verifier.FailFunc} 239 | a.IsZero() 240 | verifier.Verify(t) 241 | 242 | source = struct{ Name string }{Name: "Person"} 243 | verifier = AssertionVerifier{ShouldPass: false} 244 | a = Assertion{src: source, fail: verifier.FailFunc} 245 | a.IsZero() 246 | verifier.Verify(t) 247 | } 248 | 249 | func TestIsZeroForInt(t *testing.T) { 250 | verifier := AssertionVerifier{ShouldPass: true} 251 | a := Assertion{src: 0, fail: verifier.FailFunc} 252 | a.IsZero() 253 | verifier.Verify(t) 254 | 255 | verifier = AssertionVerifier{ShouldPass: false} 256 | a = Assertion{src: 1, fail: verifier.FailFunc} 257 | a.IsZero() 258 | verifier.Verify(t) 259 | } 260 | 261 | func TestIsZeroForFloat(t *testing.T) { 262 | verifier := AssertionVerifier{ShouldPass: true} 263 | a := Assertion{src: 0.0, fail: verifier.FailFunc} 264 | a.IsZero() 265 | verifier.Verify(t) 266 | 267 | verifier = AssertionVerifier{ShouldPass: false} 268 | a = Assertion{src: 5.1, fail: verifier.FailFunc} 269 | a.IsZero() 270 | verifier.Verify(t) 271 | } 272 | 273 | func TestIsZeroWithMessage(t *testing.T) { 274 | source := struct { 275 | Name string 276 | }{ 277 | Name: "Person", 278 | } 279 | verifier := AssertionVerifier{ShouldPass: false} 280 | a := Assertion{src: source, fail: verifier.FailFunc} 281 | a.IsZero("should be zero") 282 | verifier.Verify(t) 283 | message := "struct { Name string }{Name:\"Person\"} is not a zero value, should be zero" 284 | verifier.VerifyMessage(t, message) 285 | } 286 | 287 | func TestIsNotZeroForStructs(t *testing.T) { 288 | source := struct{ Name string }{Name: "Person"} 289 | verifier := AssertionVerifier{ShouldPass: true} 290 | a := Assertion{src: source, fail: verifier.FailFunc} 291 | a.IsNotZero() 292 | verifier.Verify(t) 293 | 294 | source = struct{ Name string }{} 295 | verifier = AssertionVerifier{ShouldPass: false} 296 | a = Assertion{src: source, fail: verifier.FailFunc} 297 | a.IsNotZero() 298 | verifier.Verify(t) 299 | } 300 | 301 | func TestIsNotZeroForInt(t *testing.T) { 302 | verifier := AssertionVerifier{ShouldPass: true} 303 | a := Assertion{src: 1, fail: verifier.FailFunc} 304 | a.IsNotZero() 305 | verifier.Verify(t) 306 | 307 | verifier = AssertionVerifier{ShouldPass: false} 308 | a = Assertion{src: 0, fail: verifier.FailFunc} 309 | a.IsNotZero() 310 | verifier.Verify(t) 311 | } 312 | 313 | func TestIsNotZeroForFloat(t *testing.T) { 314 | verifier := AssertionVerifier{ShouldPass: true} 315 | a := Assertion{src: 0.1, fail: verifier.FailFunc} 316 | a.IsNotZero() 317 | verifier.Verify(t) 318 | 319 | verifier = AssertionVerifier{ShouldPass: false} 320 | a = Assertion{src: 0.0, fail: verifier.FailFunc} 321 | a.IsNotZero() 322 | verifier.Verify(t) 323 | } 324 | 325 | func TestIsNotZeroWithMessage(t *testing.T) { 326 | source := struct{ Name string }{} 327 | verifier := AssertionVerifier{ShouldPass: false} 328 | a := Assertion{src: source, fail: verifier.FailFunc} 329 | a.IsNotZero("should not be zero") 330 | verifier.Verify(t) 331 | message := "struct { Name string }{Name:\"\"} is a zero value, should not be zero" 332 | verifier.VerifyMessage(t, message) 333 | } 334 | -------------------------------------------------------------------------------- /goblin_test.go: -------------------------------------------------------------------------------- 1 | package goblin 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "strings" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestAsync(t *testing.T) { 12 | fakeTest := testing.T{} 13 | g := Goblin(&fakeTest) 14 | 15 | g.Describe("Async test", func() { 16 | g.It("Should fail when Fail is called immediately", func(done Done) { 17 | g.Fail("Failed") 18 | }) 19 | g.It("Should fail when Fail is called", func(done Done) { 20 | go func() { 21 | time.Sleep(100 * time.Millisecond) 22 | g.Fail("foo is not bar") 23 | }() 24 | }) 25 | 26 | g.It("Should fail if done receives a parameter", func(done Done) { 27 | go func() { 28 | time.Sleep(100 * time.Millisecond) 29 | done("Error") 30 | }() 31 | }) 32 | 33 | g.It("Should pass when done is called", func(done Done) { 34 | go func() { 35 | time.Sleep(100 * time.Millisecond) 36 | done() 37 | }() 38 | }) 39 | 40 | g.It("Should fail if done has been called multiple times", func(done Done) { 41 | go func() { 42 | time.Sleep(100 * time.Millisecond) 43 | done() 44 | done() 45 | }() 46 | }) 47 | }) 48 | 49 | if !fakeTest.Failed() { 50 | t.Fatal("Failed") 51 | } 52 | } 53 | 54 | func TestAsyncSequence(t *testing.T) { 55 | fakeTest := testing.T{} 56 | g := Goblin(&fakeTest) 57 | var sequence []string 58 | 59 | g.Describe("Async test", func() { 60 | 61 | g.BeforeEach(func() { 62 | sequence = append(sequence, "global_before_each") 63 | }) 64 | 65 | g.AfterEach(func() { 66 | sequence = append(sequence, "global_after_each") 67 | }) 68 | 69 | g.Describe("nested", func() { 70 | 71 | g.BeforeEach(func() { 72 | sequence = append(sequence, "local_before_each") 73 | }) 74 | 75 | g.AfterEach(func() { 76 | sequence = append(sequence, "local_after_each") 77 | }) 78 | 79 | g.It("Should fail when Fail is called", func(done Done) { 80 | go func() { 81 | time.Sleep(100 * time.Millisecond) 82 | sequence = append(sequence, "test") 83 | done() 84 | }() 85 | }) 86 | }) 87 | 88 | }) 89 | 90 | expected := []string{ 91 | "global_before_each", 92 | "local_before_each", 93 | "test", 94 | "local_after_each", 95 | "global_after_each", 96 | } 97 | if !reflect.DeepEqual(expected, sequence) { 98 | t.Fatalf("Failed, expected:\n%s\n\ngot: %s\n", 99 | strings.Join(expected, "\n"), 100 | strings.Join(sequence, "\n"), 101 | ) 102 | } 103 | } 104 | 105 | func TestAddNumbersSucceed(t *testing.T) { 106 | fakeTest := testing.T{} 107 | g := Goblin(&fakeTest) 108 | 109 | g.Describe("Numbers", func() { 110 | g.It("Should add numbers", func() { 111 | sum := 1 + 1 112 | g.Assert(sum).Equal(2) 113 | }) 114 | }) 115 | 116 | if fakeTest.Failed() { 117 | t.Fatal("Failed") 118 | } 119 | } 120 | 121 | func TestAddNumbersFails(t *testing.T) { 122 | fakeTest := testing.T{} 123 | 124 | g := Goblin(&fakeTest) 125 | 126 | g.Describe("Numbers", func() { 127 | g.It("Should add numbers", func() { 128 | sum := 1 + 1 129 | g.Assert(sum).Equal(4) 130 | }) 131 | }) 132 | 133 | if !fakeTest.Failed() { 134 | t.Fatal("Failed") 135 | } 136 | } 137 | 138 | func TestMultipleIts(t *testing.T) { 139 | fakeTest := testing.T{} 140 | 141 | g := Goblin(&fakeTest) 142 | 143 | count := 0 144 | g.Describe("Numbers", func() { 145 | g.It("Should add numbers", func() { 146 | count++ 147 | sum := 1 + 1 148 | g.Assert(sum).Equal(2) 149 | }) 150 | 151 | g.It("Should add numbers", func() { 152 | count++ 153 | sum := 1 + 1 154 | g.Assert(sum).Equal(4) 155 | }) 156 | }) 157 | 158 | if count != 2 { 159 | t.Fatal("Failed") 160 | } 161 | } 162 | 163 | func TestMultipleDescribes(t *testing.T) { 164 | fakeTest := testing.T{} 165 | 166 | g := Goblin(&fakeTest) 167 | 168 | count := 0 169 | g.Describe("Numbers", func() { 170 | 171 | g.Describe("Addition", func() { 172 | g.It("Should add numbers", func() { 173 | count++ 174 | sum := 1 + 1 175 | g.Assert(sum).Equal(2) 176 | }) 177 | }) 178 | 179 | g.Describe("Subtraction", func() { 180 | g.It("Should subtract numbers", func() { 181 | count++ 182 | sub := 5 - 5 183 | g.Assert(sub).Equal(1) 184 | }) 185 | }) 186 | }) 187 | 188 | if count != 2 { 189 | t.Fatal("Failed") 190 | } 191 | } 192 | 193 | func TestPending(t *testing.T) { 194 | fakeTest := testing.T{} 195 | 196 | g := Goblin(&fakeTest) 197 | 198 | g.Describe("Numbers", func() { 199 | 200 | g.It("Should add numbers") 201 | 202 | g.Describe("Subtraction", func() { 203 | g.It("Should subtract numbers") 204 | }) 205 | 206 | }) 207 | 208 | if fakeTest.Failed() { 209 | t.Fatal("Failed") 210 | } 211 | } 212 | 213 | func TestExcluded(t *testing.T) { 214 | fakeTest := testing.T{} 215 | 216 | g := Goblin(&fakeTest) 217 | 218 | count := 0 219 | g.Describe("Numbers", func() { 220 | 221 | g.Xit("Should add numbers", func() { 222 | count++ 223 | sum := 1 + 1 224 | g.Assert(sum).Equal(2) 225 | }) 226 | 227 | g.Describe("Subtraction", func() { 228 | g.Xit("Should subtract numbers", func() { 229 | count++ 230 | sub := 5 - 5 231 | g.Assert(sub).Equal(1) 232 | }) 233 | }) 234 | 235 | }) 236 | 237 | if count != 0 { 238 | t.Fatal("Failed") 239 | } 240 | 241 | if fakeTest.Failed() { 242 | t.Fatal("Failed") 243 | } 244 | } 245 | 246 | func TestJustBeforeEach(t *testing.T) { 247 | fakeTest := testing.T{} 248 | 249 | g := Goblin(&fakeTest) 250 | const ( 251 | before = iota 252 | beforeEach 253 | nBeforeEach 254 | justBeforeEach 255 | nJustBeforeEach 256 | it 257 | nIt 258 | ) 259 | 260 | var ( 261 | res [9]int 262 | i int 263 | ) 264 | 265 | g.Describe("Outer", func() { 266 | g.Before(func() { 267 | res[i] = before 268 | i++ 269 | }) 270 | 271 | g.BeforeEach(func() { 272 | res[i] = beforeEach 273 | i++ 274 | }) 275 | 276 | g.JustBeforeEach(func() { 277 | res[i] = justBeforeEach 278 | i++ 279 | }) 280 | 281 | g.It("should run all before handles by now", func() { 282 | res[i] = it 283 | i++ 284 | }) 285 | 286 | g.Describe("Nested", func() { 287 | g.BeforeEach(func() { 288 | res[i] = nBeforeEach 289 | i++ 290 | }) 291 | 292 | g.JustBeforeEach(func() { 293 | res[i] = nJustBeforeEach 294 | i++ 295 | }) 296 | 297 | g.It("should run all before handles by now", func() { 298 | res[i] = nIt 299 | i++ 300 | }) 301 | }) 302 | }) 303 | 304 | expected := [...]int{ 305 | before, 306 | beforeEach, 307 | justBeforeEach, 308 | it, 309 | beforeEach, 310 | nBeforeEach, 311 | justBeforeEach, 312 | nJustBeforeEach, 313 | nIt, 314 | } 315 | 316 | if res != expected { 317 | t.Fatalf("expected %v to equal %v", res, expected) 318 | } 319 | } 320 | 321 | func TestNotRunBeforesOrAfters(t *testing.T) { 322 | fakeTest := testing.T{} 323 | 324 | g := Goblin(&fakeTest) 325 | var count int 326 | 327 | g.Describe("Numbers", func() { 328 | g.Before(func() { 329 | count++ 330 | }) 331 | 332 | g.BeforeEach(func() { 333 | count++ 334 | }) 335 | 336 | g.JustBeforeEach(func() { 337 | count++ 338 | }) 339 | 340 | g.After(func() { 341 | count++ 342 | }) 343 | g.AfterEach(func() { 344 | count++ 345 | }) 346 | 347 | g.Describe("Letters", func() { 348 | g.Before(func() { 349 | count++ 350 | }) 351 | 352 | g.BeforeEach(func() { 353 | count++ 354 | }) 355 | 356 | g.JustBeforeEach(func() { 357 | count++ 358 | }) 359 | 360 | g.After(func() { 361 | count++ 362 | }) 363 | g.AfterEach(func() { 364 | count++ 365 | }) 366 | }) 367 | }) 368 | 369 | if count != 0 { 370 | t.Fatal("Failed") 371 | } 372 | } 373 | 374 | func TestFailOnError(t *testing.T) { 375 | fakeTest := testing.T{} 376 | 377 | g := Goblin(&fakeTest) 378 | 379 | g.Describe("Numbers", func() { 380 | g.It("Does something", func() { 381 | g.Fail("Something") 382 | }) 383 | }) 384 | 385 | g.Describe("Errors", func() { 386 | g.It("Should fail with structs", func() { 387 | var s struct{ error string } 388 | s.error = "Error" 389 | g.Fail(s) 390 | }) 391 | }) 392 | 393 | if !fakeTest.Failed() { 394 | t.Fatal("Failed") 395 | } 396 | } 397 | 398 | func TestFailfOnError(t *testing.T) { 399 | fakeTest := testing.T{} 400 | 401 | g := Goblin(&fakeTest) 402 | 403 | g.Describe("Numbers", func() { 404 | g.It("Does something", func() { 405 | g.Failf("Something goes %s", "wrong") 406 | }) 407 | }) 408 | 409 | if !fakeTest.Failed() { 410 | t.Fatal("Failed") 411 | } 412 | } 413 | 414 | func TestRegex(t *testing.T) { 415 | fakeTest := testing.T{} 416 | os.Args = append(os.Args, "-goblin.run=matches") 417 | parseFlags() 418 | g := Goblin(&fakeTest) 419 | 420 | g.Describe("Test", func() { 421 | g.It("Doesn't match regex", func() { 422 | g.Fail("Regex shouldn't match") 423 | }) 424 | 425 | g.It("It matches regex", func() {}) 426 | g.It("It also matches", func() {}) 427 | }) 428 | 429 | if fakeTest.Failed() { 430 | t.Fatal("Failed") 431 | } 432 | 433 | // Reset the regex so other tests can run 434 | runRegex = nil 435 | } 436 | 437 | func TestFailImmediately(t *testing.T) { 438 | fakeTest := testing.T{} 439 | g := Goblin(&fakeTest) 440 | reached := false 441 | g.Describe("Errors", func() { 442 | g.It("Should fail immediately for sync test", func() { 443 | g.Assert(false).IsTrue() 444 | reached = true 445 | g.Assert("foo").Equal("bar") 446 | }) 447 | g.It("Should fail immediately for async test", func(done Done) { 448 | go func() { 449 | g.Assert(false).IsTrue() 450 | reached = true 451 | g.Assert("foo").Equal("bar") 452 | done() 453 | }() 454 | }) 455 | }) 456 | 457 | if reached { 458 | t.Fatal("Failed") 459 | } 460 | } 461 | 462 | func TestTimeout(t *testing.T) { 463 | fakeTest := testing.T{} 464 | os.Args = append(os.Args, "-goblin.timeout=10ms", "-goblin.run=") 465 | parseFlags() 466 | g := Goblin(&fakeTest) 467 | 468 | g.Describe("Test", func() { 469 | g.It("Should fail if test exceeds the specified timeout with sync test", func() { 470 | time.Sleep(100 * time.Millisecond) 471 | }) 472 | 473 | g.It("Should fail if test exceeds the specified timeout with async test", func(done Done) { 474 | time.Sleep(100 * time.Millisecond) 475 | done() 476 | }) 477 | }) 478 | 479 | if !fakeTest.Failed() { 480 | t.Fatal("Failed") 481 | } 482 | } 483 | 484 | func TestItTimeout(t *testing.T) { 485 | fakeTest := testing.T{} 486 | os.Args = append(os.Args, "-goblin.timeout=10ms") 487 | parseFlags() 488 | g := Goblin(&fakeTest) 489 | 490 | g.Describe("Test", func() { 491 | g.It("Should override default timeout", func() { 492 | g.Timeout(20 * time.Millisecond) 493 | time.Sleep(15 * time.Millisecond) 494 | }) 495 | 496 | g.It("Should revert for different it", func() { 497 | g.Assert(g.timeout).Equal(10 * time.Millisecond) 498 | }) 499 | 500 | }) 501 | if fakeTest.Failed() { 502 | t.Fatal("Failed") 503 | } 504 | } 505 | 506 | func TestIsNilAndIsNotNil(t *testing.T) { 507 | fakeTest := testing.T{} 508 | g := Goblin(&fakeTest) 509 | 510 | g.Describe("Test for IsNil", func() { 511 | g.It("Should assert successfully with nil value", func() { 512 | g.Assert(nil).IsNil() 513 | }) 514 | }) 515 | 516 | g.Describe("Test for IsNotNil", func() { 517 | g.It("Should assert successfully with not nil value", func() { 518 | g.Assert(struct{}{}).IsNotNil() 519 | }) 520 | }) 521 | 522 | if fakeTest.Failed() { 523 | t.Fatal("Failed") 524 | } 525 | 526 | g.Describe("Test for IsNil with failed assertion", func() { 527 | g.It("Should fail", func() { 528 | g.Assert(100).IsNil() 529 | }) 530 | }) 531 | 532 | g.Describe("Test for IsNotNil with failed assertion", func() { 533 | g.It("Should fail", func() { 534 | g.Assert(nil).IsNotNil() 535 | }) 536 | }) 537 | 538 | if !fakeTest.Failed() { 539 | t.Fatal("Failed") 540 | } 541 | } 542 | 543 | func TestIsZeroAndIsNotZero(t *testing.T) { 544 | fakeTest := testing.T{} 545 | g := Goblin(&fakeTest) 546 | 547 | g.Describe("Test for IsZero", func() { 548 | g.It("Should assert successfully with int zero value", func() { 549 | g.Assert(0).IsZero() 550 | }) 551 | 552 | g.It("Should assert successfully with float zero value", func() { 553 | g.Assert(0.0).IsZero() 554 | }) 555 | 556 | g.It("Should assert successfully with string zero value", func() { 557 | g.Assert("").IsZero() 558 | }) 559 | 560 | g.It("Should assert successfully with struct zero value", func() { 561 | g.Assert(struct{}{}).IsZero() 562 | }) 563 | 564 | g.It("Should assert successfully with struct field with zero value", func() { 565 | g.Assert(struct{ value int }{value: 0}).IsZero() 566 | }) 567 | }) 568 | 569 | g.Describe("Test for IsNotZero", func() { 570 | g.It("Should assert successfully with int not zero value", func() { 571 | g.Assert(1).IsNotZero() 572 | }) 573 | 574 | g.It("Should assert successfully with float not zero value", func() { 575 | g.Assert(0.5).IsNotZero() 576 | }) 577 | 578 | g.It("Should assert successfully with string not zero value", func() { 579 | g.Assert("ABC").IsNotZero() 580 | }) 581 | 582 | g.It("Should assert successfully with struct not zero value", func() { 583 | g.Assert(struct{ value int }{value: 1}).IsNotZero() 584 | }) 585 | }) 586 | 587 | if fakeTest.Failed() { 588 | t.Fatal("Failed") 589 | } 590 | 591 | g.Describe("Test for IsZero with failed assertion", func() { 592 | g.It("Should fail", func() { 593 | g.Assert(100).IsZero() 594 | }) 595 | 596 | g.It("Should fail", func() { 597 | g.Assert(1.0).IsZero() 598 | }) 599 | 600 | g.It("Should fail", func() { 601 | g.Assert("A").IsZero() 602 | }) 603 | 604 | g.It("Should fail", func() { 605 | g.Assert(struct{ value int }{value: 1}).IsZero() 606 | }) 607 | }) 608 | 609 | g.Describe("Test for IsNotZero with failed assertion", func() { 610 | g.It("Should fail", func() { 611 | g.Assert(0).IsNotZero() 612 | }) 613 | 614 | g.It("Should fail", func() { 615 | g.Assert(0.0).IsNotZero() 616 | }) 617 | 618 | g.It("Should fail", func() { 619 | g.Assert("").IsNotZero() 620 | }) 621 | 622 | g.It("Should fail", func() { 623 | g.Assert(struct{}{}).IsNotZero() 624 | }) 625 | }) 626 | 627 | if !fakeTest.Failed() { 628 | t.Fatal("Failed") 629 | } 630 | } 631 | --------------------------------------------------------------------------------