├── LICENSE ├── Makefile ├── README.md ├── bdd.go ├── examples ├── before-after.go ├── errors.go ├── expect.go ├── simple-stdout.go ├── simple-with-boilerplate.go ├── simple.go └── syntax-errors.go ├── expect.go ├── install ├── mao ├── out.go └── test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Azer Koçulu 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | link: 2 | @echo "Linking local copy to /usr/bin" 3 | @mv /usr/bin/mao /tmp/mao.bak 4 | @ln ./mao /usr/bin/mao 5 | @echo "Done" 6 | 7 | unlink: 8 | @mv /tmp/mao.bak /usr/bin/mao 9 | @echo "Done!" 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Māo 2 | 3 | Pragmatic BDD Testing Framework For Go. 4 | 5 | ```go 6 | import "math" 7 | 8 | Desc("math.Abs", func(it It) { 9 | it("returns the absolute value of x", func(expect Expect) { 10 | expect(math.Abs(-12)).Equal(12.0) 11 | expect(math.Abs(-.5)).Equal(0.5) 12 | }) 13 | }) 14 | ``` 15 | 16 | **Why Māo?** 17 | 18 | * More flexibility: You can locate your tests in any directory. 19 | * Don't repeat yourself: Māo does the ceremony for you by preprocessing the test module. 20 | * Minimalistic Output: Māo doesn't have verbose outputs. It'll output short and briefly, will show you what lines fail in case of any error. 21 | 22 | Update: **[Development Status](#development-status)** 23 | 24 | ## Install 25 | 26 | Install the Go library first; 27 | 28 | ```bash 29 | $ go install github.com/azer/mao 30 | ``` 31 | 32 | And optionally, install the command-line tool to avoid the boilerplate code: *See examples/simple-with-boilerplate.go if you'd like to skip this step* 33 | 34 | ```bash 35 | $ curl https://raw.github.com/azer/mao/master/install | sh 36 | ``` 37 | 38 | ## Usage 39 | 40 | Create a test module anywhere you want and import the code you'd like to test: 41 | 42 | ```go 43 | import "math/rand" // or: import "github.com/you/repository" 44 | 45 | Desc("math.Abs", func(it It) { 46 | it("returns the absolute value of x", func(expect Expect) { 47 | expect(math.Abs(-12)).Equal(12.0) 48 | expect(math.Abs(-.5)).Equal(0.5) 49 | }) 50 | }) 51 | ``` 52 | 53 | And run it with `mao` command: 54 | 55 | ```bash 56 | $ mao test.go 57 | ``` 58 | 59 | It'll either output a simple success message that will look like; 60 | 61 | ``` 62 | Ran 3 tests successfully. 63 | ``` 64 | 65 | Or some error messages with failing source code lines, like following; 66 | 67 | ![](https://i.cloudup.com/CHNocClka1.png) 68 | 69 | ## Reference 70 | 71 | ### BDD API 72 | 73 | * Desc 74 | * It 75 | * BeforeEach 76 | * AfterEach 77 | 78 | ### Assertion API 79 | 80 | * Above 81 | * Equal 82 | * NotEqual 83 | * NotExist 84 | * ResponseBody 85 | * Lower 86 | 87 | ## Development Status 88 | 89 | I recently think of two major changes in the project; 90 | 91 | * Rewriting [the preprocesser](https://github.com/azer/mao/blob/master/mao) (bash script) in Go 92 | * Replacing "desc/it" with "test". 93 | 94 | So it'd be looking like; 95 | 96 | ```go 97 | import "math" 98 | 99 | Test("returns the absolute value of x", func(e Expect) { 100 | expect(math.Abs(-12)).Equal(12.0) 101 | expect(math.Abs(-.5)).Equal(0.5) 102 | }) 103 | ``` 104 | 105 | At this point, I wouldn't recommend to use Mao. I still have the same goal but it may cause some pain since its preprocesser is not reliable enough. 106 | 107 | And since other projects occupy my time nowadays, I'd appreciate pull requests to reach the goals of the project. Thanks for your interest! 108 | -------------------------------------------------------------------------------- /bdd.go: -------------------------------------------------------------------------------- 1 | package mao 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type BeforeAfterFn func() 8 | type It func(title string, fn func(Expect)) 9 | 10 | var ( 11 | Describe func (desc string, wrapper func(It)) = Desc 12 | beforeEachFn BeforeAfterFn 13 | afterEachFn BeforeAfterFn 14 | ) 15 | 16 | func Desc(desc string, wrapper func(It)) { 17 | beforeEachFn = nil 18 | afterEachFn = nil 19 | 20 | wrapper(func(it string, fn func(Expect)) { 21 | test := Test{fmt.Sprintf("%s %s", desc, it), fn, beforeEachFn, afterEachFn} 22 | test.Run() 23 | }) 24 | } 25 | 26 | func AfterEach(fn BeforeAfterFn) { 27 | afterEachFn = fn 28 | } 29 | 30 | func BeforeEach(fn BeforeAfterFn) { 31 | beforeEachFn = fn 32 | } 33 | -------------------------------------------------------------------------------- /examples/before-after.go: -------------------------------------------------------------------------------- 1 | import "math/rand" 2 | 3 | Desc("randInt", func(it It) { 4 | var ( 5 | current int 6 | last int 7 | ) 8 | 9 | BeforeEach(func () { 10 | current = rand.Int() 11 | }) 12 | 13 | AfterEach(func () { 14 | last = current 15 | }) 16 | 17 | it("generates a random number", func (expect Expect) { 18 | expect(current).Above(0) 19 | }) 20 | 21 | it("generates a unique number on each call", func (expect Expect) { 22 | expect(current).Above(0) 23 | expect(last).Above(0) 24 | expect(current).NotEqual(last) 25 | }) 26 | 27 | }) 28 | -------------------------------------------------------------------------------- /examples/errors.go: -------------------------------------------------------------------------------- 1 | Desc("math.Abs", func(it It) { 2 | 3 | it("returns the absolute value of x", func(expect Expect) { 4 | expect(1).Equal(2) 5 | expect(0.5).Equal(0.9) 6 | }) 7 | 8 | it("should return x if is positive", func(expect Expect) { 9 | var ( 10 | a *int 11 | b interface{} 12 | c int = 5 13 | ) 14 | 15 | expect(a).NotExist() 16 | expect(b).NotExist() 17 | expect(c).NotExist() 18 | }) 19 | 20 | }) 21 | 22 | Desc("math.Floor", func(it It) { 23 | 24 | it("returns the greatest integer value less than or equal to x", func(expect Expect) { 25 | expect(1).Equal(2) 26 | expect(0.5).Equal(0.5) 27 | }) 28 | 29 | it("should return x if is int", func(expect Expect) { 30 | expect(13).Equal(13.0) 31 | }) 32 | 33 | }) 34 | -------------------------------------------------------------------------------- /examples/expect.go: -------------------------------------------------------------------------------- 1 | Desc("Expect", func (it It) { 2 | it("has an above method to assert target is greater than value", func (expect Expect) { 3 | expect(5).Above(3) 4 | }) 5 | 6 | it("has an equal method to assert target is greater than value", func (expect Expect) { 7 | expect(5).Equal(5) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /examples/simple-stdout.go: -------------------------------------------------------------------------------- 1 | import ( 2 | "math" 3 | "fmt" 4 | ) 5 | 6 | Desc("math.Abs", func(it It) { 7 | it("returns the absolute value of x", func(expect Expect) { 8 | fmt.Println("before") 9 | expect(math.Abs(-12)).Equal(12.0) 10 | expect(math.Abs(-.5)).Equal(0.5) 11 | fmt.Println("after") 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /examples/simple-with-boilerplate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "math" 4 | import . "github.com/azer/mao" 5 | 6 | func main() { 7 | 8 | Desc("math.Abs", func(it It) { 9 | 10 | it("returns the absolute value of x", func(expect Expect) { 11 | expect(math.Abs(-12)).Equal(12.0) 12 | expect(math.Abs(-.5)).Equal(0.5) 13 | }) 14 | 15 | }) 16 | 17 | Desc("math.Floor", func(it It) { 18 | 19 | it("returns the greatest integer value less than or equal to x", func(expect Expect) { 20 | expect(math.Floor(13.4)).Equal(13.0) 21 | expect(math.Floor(13.99)).Equal(13.0) 22 | }) 23 | 24 | it("should return x if is int", func(expect Expect) { 25 | expect(math.Floor(13)).Equal(13.0) 26 | }) 27 | 28 | }) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /examples/simple.go: -------------------------------------------------------------------------------- 1 | import "math" 2 | 3 | Desc("math.Abs", func(it It) { 4 | it("returns the absolute value of x", func(expect Expect) { 5 | expect(math.Abs(-12)).Equal(12.0) 6 | expect(math.Abs(-.5)).Equal(0.5) 7 | }) 8 | }) 9 | 10 | Desc("math.Floor", func(it It) { 11 | it("returns the greatest integer value less than or equal to x", func(expect Expect) { 12 | expect(math.Floor(13.4)).Equal(13.0) 13 | expect(math.Floor(13.99)).Equal(13.0) 14 | }) 15 | 16 | it("should return x if is int", func(expect Expect) { 17 | expect(math.Floor(13)).Equal(13.0) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /examples/syntax-errors.go: -------------------------------------------------------------------------------- 1 | Desc("syntax errors", func(it It) { 2 | 3 | it("should appear correctly", func(expect Expect) { 4 | a := 123 5 | }) 6 | 7 | it("should appear correctly", func(expect Expect) { 8 | // b := 9 | }) 10 | 11 | it("should appear correctly", func(expect Expect) { 12 | c := 456 13 | }) 14 | 15 | }) 16 | -------------------------------------------------------------------------------- /expect.go: -------------------------------------------------------------------------------- 1 | package mao 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "net/http" 7 | "io/ioutil" 8 | ) 9 | 10 | type Expect func(val interface{}) *Expected 11 | 12 | type Expected struct { 13 | Scope *Test 14 | Value interface{} 15 | } 16 | 17 | func (self *Expected) Above(b interface{}) { 18 | value, ok := self.Value.(int) 19 | otherValue, otherOk := b.(int) 20 | 21 | if !ok || !otherOk { 22 | self.Scope.PrintError(fmt.Sprintf("Unable to compare `%v` with `%v`", self.Value, b)) 23 | return 24 | } 25 | 26 | if value <= otherValue { 27 | self.Scope.PrintError(fmt.Sprintf("Expected `%v` to be above than `%v`", self.Value, b)) 28 | } 29 | } 30 | 31 | func (self *Expected) Equal(b interface{}) { 32 | if self.Value != b { 33 | self.Scope.PrintError(fmt.Sprintf("Expected `%v` to equal `%v`", self.Value, b)) 34 | } 35 | } 36 | 37 | func (self *Expected) Lower(b interface{}) { 38 | value, ok := self.Value.(int) 39 | otherValue, otherOk := b.(int) 40 | 41 | if !ok || !otherOk { 42 | self.Scope.PrintError(fmt.Sprintf("Unable to compare `%v` with `%v`", self.Value, b)) 43 | return 44 | } 45 | 46 | if value >= otherValue { 47 | self.Scope.PrintError(fmt.Sprintf("Expected `%v` to be lower than `%v`", self.Value, b)) 48 | } 49 | } 50 | 51 | func (self *Expected) NotEqual(b interface{}) { 52 | if self.Value == b { 53 | self.Scope.PrintError(fmt.Sprintf("Expected `%v` to not equal `%v`", self.Value, b)) 54 | } 55 | } 56 | 57 | func (self *Expected) NotExist() { 58 | 59 | msg := fmt.Sprintf("Expected `%v` to not exist.", self.Value) 60 | 61 | if self.Value == nil { 62 | return 63 | } 64 | 65 | if self.Value != nil { 66 | self.Scope.PrintError(msg) 67 | return 68 | } 69 | 70 | v := reflect.ValueOf(self.Value) 71 | 72 | fmt.Println("value:", self.Value, "value == nil", self.Value == nil, " v.isNil?", v.IsNil()) 73 | 74 | if !v.IsNil() { 75 | self.Scope.PrintError(msg) 76 | } 77 | } 78 | 79 | func (self *Expected) ResponseBody(b interface{}) { 80 | response, err := http.Get(self.Value.(string)) 81 | 82 | if err != nil { 83 | self.Scope.PrintError(fmt.Sprintf("Unable to get %s", self.Value)) 84 | } 85 | 86 | body, err := ioutil.ReadAll(response.Body) 87 | 88 | if err != nil { 89 | self.Scope.PrintError(fmt.Sprintf("Unable to read `%v`", self.Value)) 90 | return 91 | } 92 | 93 | if string(body) != b { 94 | self.Scope.PrintError(fmt.Sprintf("Expected `%v` to equal `%v`", string(body), b)) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /install: -------------------------------------------------------------------------------- 1 | echo "" 2 | echo "\x1B[90m This script requires superuser access to install Mao." 3 | echo " You will be prompted for your password by sudo.\033[0m" 4 | echo "" 5 | 6 | sudo -k 7 | 8 | sudo sh <