├── LICENSE ├── README.md ├── bdd.go ├── bdd_test.go ├── formatter.go ├── matcher_test.go └── matchers.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Pranav Raja. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Zen 2 | 3 | BDD Testing Framework For Go. 4 | 5 | `go get github.com/pranavraja/zen` 6 | 7 | Forked from [github.com/azer/mao](https://github.com/azer/mao). 8 | 9 | Changes in my fork: 10 | - Print each behaviour as it's being verified 11 | - Actually fail the testrunner when there are expectation failures 12 | - Small API changes 13 | - Allow custom matchers 14 | 15 | ```go 16 | package zen 17 | 18 | import ( 19 | "testing" 20 | . "github.com/pranavraja/zen" 21 | ) 22 | 23 | func TestZen(t *testing.T) { 24 | Desc(t, "zen", func(it It) { 25 | it("should know when things exist", func(expect Expect) { 26 | expect("tree").ToExist() 27 | }) 28 | it("should know when things don't exist", func(expect Expect) { 29 | expect(nil).ToNotExist() 30 | }) 31 | it("should know that a thing is equal to itself", func(expect Expect) { 32 | expect(1).ToEqual(1) 33 | }) 34 | it("should know when things are different", func(expect Expect) { 35 | expect(1).ToNotEqual(2) 36 | }) 37 | it("should be able to learn about new things", func (expect Expect) { 38 | divisibleBy := func (a, b interface{}) bool { 39 | return a.(int) % b.(int) == 0 40 | } 41 | expect(1).To("be divisible by", divisibleBy, 1) 42 | }) 43 | }) 44 | } 45 | ``` 46 | 47 | ## Output 48 | 49 | Pass: 50 | 51 | ![Test passed](http://i.imgur.com/zmkJOTW.png) 52 | 53 | Fail: 54 | 55 | ![Test failure](http://i.imgur.com/CWiy8wi.png) 56 | 57 | 58 | ## Before and After test setup 59 | 60 | ```go 61 | package zen 62 | 63 | import ( 64 | "testing" 65 | . "github.com/pranavraja/zen" 66 | ) 67 | 68 | func TestZen(t *testing.T) { 69 | Desc(t, "before and after", func(it It) { 70 | count := 0 71 | 72 | before := func() { 73 | count++ 74 | } 75 | 76 | after := func() { 77 | count-- 78 | } 79 | 80 | setup := Setup(before, after) 81 | 82 | it("should execute before and after functions", setup(func(expect Expect) { 83 | expect(count).ToEqual(1) 84 | })) 85 | 86 | it("should execute before and after functions", setup(func(expect Expect) { 87 | expect(count).ToEqual(1) 88 | })) 89 | }) 90 | } 91 | ``` 92 | -------------------------------------------------------------------------------- /bdd.go: -------------------------------------------------------------------------------- 1 | package zen 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | type Test struct { 9 | T *testing.T 10 | Title string 11 | Fn func(Expect) 12 | } 13 | 14 | func (test *Test) Run() { 15 | test.Fn(func(val interface{}) *Expectation { 16 | return &Expectation{test, val} 17 | }) 18 | } 19 | 20 | type It func(title string, fn func(Expect)) 21 | 22 | func Desc(t *testing.T, desc string, wrapper func(It)) { 23 | wrapper(func(it string, fn func(Expect)) { 24 | test := Test{t, fmt.Sprintf("%s %s", desc, it), fn} 25 | test.Run() 26 | }) 27 | 28 | } 29 | 30 | func Setup(before, after func()) func(fn func(Expect)) func(Expect) { 31 | return func(fn func(Expect)) func(Expect) { 32 | before() 33 | return func(expect Expect) { 34 | fn(expect) 35 | after() 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /bdd_test.go: -------------------------------------------------------------------------------- 1 | package zen 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestExampleDesc(t *testing.T) { 8 | Desc(t, "Equality", func(it It) { 9 | it("any integer should equal itself", func(expect Expect) { 10 | expect(1).ToEqual(1) 11 | }) 12 | it("any integer should not equal nil", func(expect Expect) { 13 | expect(1).ToNotEqual(nil) 14 | expect(1).ToExist() // Same as above 15 | }) 16 | }) 17 | } 18 | 19 | func TestSetupAndTeardown(t *testing.T) { 20 | count := 0 21 | 22 | before := func() { 23 | count++ 24 | } 25 | 26 | after := func() { 27 | count-- 28 | } 29 | 30 | setup := Setup(before, after) 31 | 32 | Desc(t, "Setup", func(it It) { 33 | it("should execute before", setup(func(expect Expect) { 34 | expect(count).ToEqual(1) 35 | })) 36 | 37 | if count != 0 { 38 | t.Error("Count should have been reset to zero by the teardown func") 39 | } 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /formatter.go: -------------------------------------------------------------------------------- 1 | package zen 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "path" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | type failingLine struct { 12 | content string 13 | filename string 14 | next string 15 | number int 16 | prev string 17 | } 18 | 19 | var lastTitle string 20 | 21 | var ( 22 | reset string = "\033[0m" 23 | white string = "\033[37m\033[1m" 24 | grey string = "\x1B[90m" 25 | red string = "\033[31m\033[1m" 26 | ) 27 | 28 | func (test *Test) PrintTitle() { 29 | if lastTitle == test.Title { 30 | return 31 | } 32 | fmt.Printf("\033[37m \033[1m %s \n", test.Title) 33 | lastTitle = test.Title 34 | } 35 | 36 | func (test *Test) PrintError(message string) { 37 | test.PrintTitle() 38 | failingLine, err := getFailingLine() 39 | 40 | if err != nil { 41 | return 42 | } 43 | 44 | fmt.Printf("%s %s %s %s %s\n", red, message, grey, path.Base(failingLine.filename), reset) 45 | test.PrintFailingLine(&failingLine) 46 | test.T.Fail() 47 | } 48 | 49 | func (test *Test) PrintFailingLine(failingLine *failingLine) { 50 | fmt.Printf("%s %d. %s\n", grey, failingLine.number-1, failingLine.prev) 51 | fmt.Printf("%s %d. %s %s\n", white, failingLine.number, failingLine.content, reset) 52 | fmt.Printf("%s %d. %s\n", grey, failingLine.number+1, failingLine.next) 53 | fmt.Println(reset) 54 | } 55 | 56 | func getFailingLine() (failingLine, error) { 57 | _, filename, ln, _ := runtime.Caller(3) 58 | // TODO: this is really hacky, need to find a way of not using magic numbers for runtime.Caller 59 | // If we are not in a test file, we must still be inside this package, 60 | // so we need to go up one more stack frame to get to the test file 61 | if !strings.HasSuffix(filename, "_test.go") { 62 | _, filename, ln, _ = runtime.Caller(4) 63 | } 64 | 65 | bf, err := ioutil.ReadFile(filename) 66 | 67 | if err != nil { 68 | return failingLine{}, fmt.Errorf("Failed to open %s", filename) 69 | } 70 | 71 | lines := strings.Split(string(bf), "\n")[ln-2 : ln+2] 72 | 73 | return failingLine{ 74 | softTabs(lines[1]), 75 | filename, 76 | softTabs(lines[2]), 77 | int(ln), 78 | softTabs(lines[0]), 79 | }, nil 80 | 81 | } 82 | 83 | func softTabs(text string) string { 84 | return strings.Replace(text, "\t", " ", -1) 85 | } 86 | -------------------------------------------------------------------------------- /matcher_test.go: -------------------------------------------------------------------------------- 1 | package zen 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func differsByOne(a, b interface{}) bool { 8 | diff := a.(int) - b.(int) 9 | return diff == 1 || diff == -1 10 | } 11 | 12 | type output struct { 13 | titlePrinted bool 14 | errors string 15 | } 16 | 17 | func (out *output) PrintTitle() { 18 | out.titlePrinted = true 19 | } 20 | func (out *output) PrintError(err string) { 21 | out.errors += err 22 | } 23 | 24 | // Prints title, even when test passes 25 | func TestMatcherPrintsTitle(t *testing.T) { 26 | out := new(output) 27 | expectation := &Expectation{out, 1} 28 | expectation.ToEqual(1) 29 | if titlePrinted := out.titlePrinted; !titlePrinted { 30 | t.Errorf("should have printed title, but didn't") 31 | } 32 | } 33 | 34 | // Prints custom error message 35 | func TestCustomMatcher(t *testing.T) { 36 | out := new(output) 37 | expectation := &Expectation{out, 1} 38 | expectation.To("differ by one from", differsByOne, 10) 39 | if err := out.errors; err != "Expected `1` to differ by one from `10`" { 40 | t.Errorf("incorrect error output for custom matcher: %s", err) 41 | } 42 | } 43 | 44 | func TestCustomMatcherPassesWithNoError(t *testing.T) { 45 | out := new(output) 46 | expectation := &Expectation{out, 1} 47 | expectation.To("differ by one from", differsByOne, 2) 48 | if err := out.errors; err != "" { 49 | t.Errorf("non-blank error '%s' even though it should have passed", err) 50 | } 51 | } 52 | 53 | var expect Expect 54 | 55 | func ExampleExpectation_To() { 56 | divisibleBy := func(a, b interface{}) bool { 57 | return a.(int)%b.(int) == 0 58 | } 59 | expect(9).To("be divisible by", divisibleBy, 3) 60 | } 61 | -------------------------------------------------------------------------------- /matchers.go: -------------------------------------------------------------------------------- 1 | package zen 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Expect func(val interface{}) *Expectation 8 | 9 | type formatter interface { 10 | PrintTitle() 11 | PrintError(string) 12 | } 13 | 14 | type Expectation struct { 15 | Output formatter 16 | Value interface{} 17 | } 18 | 19 | type Matcher func(a, b interface{}) bool 20 | 21 | func Equal(a, b interface{}) bool { 22 | return a == b 23 | } 24 | 25 | func NotEqual(a, b interface{}) bool { 26 | return a != b 27 | } 28 | 29 | func NotExist(a, b interface{}) bool { 30 | return a == nil 31 | } 32 | 33 | func Exist(a, b interface{}) bool { 34 | return a != nil 35 | } 36 | 37 | func (self *Expectation) To(desc string, match Matcher, value interface{}) { 38 | self.Output.PrintTitle() 39 | if !match(self.Value, value) { 40 | self.Output.PrintError(fmt.Sprintf("Expected `%v` to %s `%v`", self.Value, desc, value)) 41 | } 42 | } 43 | 44 | func (self *Expectation) ToEqual(b interface{}) { 45 | self.To("equal", Equal, b) 46 | } 47 | 48 | func (self *Expectation) ToNotEqual(b interface{}) { 49 | self.To("not equal", NotEqual, b) 50 | } 51 | 52 | func (self *Expectation) ToExist() { 53 | self.To("exist", Exist, nil) 54 | } 55 | 56 | func (self *Expectation) ToNotExist() { 57 | self.To("not exist", NotExist, nil) 58 | } 59 | --------------------------------------------------------------------------------