github.com/onsi/gomega@v1.32.0/matchers/have_field_test.go (about) 1 package matchers_test 2 3 import ( 4 "fmt" 5 "time" 6 7 . "github.com/onsi/ginkgo/v2" 8 . "github.com/onsi/gomega" 9 ) 10 11 type Book struct { 12 Title string 13 Author person 14 Pages int 15 Sequel *Book 16 Prequel *Book 17 } 18 19 func (book Book) AuthorName() string { 20 return fmt.Sprintf("%s %s", book.Author.FirstName, book.Author.LastName) 21 } 22 23 func (book Book) AbbreviatedAuthor() person { 24 return person{ 25 FirstName: book.Author.FirstName[0:3], 26 LastName: book.Author.LastName[0:3], 27 DOB: book.Author.DOB, 28 } 29 } 30 31 func (book Book) ReceiverTitle() string { 32 return book.Title 33 } 34 35 func (book *Book) PointerReceiverTitle() string { 36 return book.Title 37 } 38 39 func (book Book) NoReturn() { 40 } 41 42 func (book Book) TooManyReturn() (string, error) { 43 return "", nil 44 } 45 46 func (book Book) HasArg(arg string) string { 47 return arg 48 } 49 50 type person struct { 51 FirstName string 52 LastName string 53 DOB time.Time 54 } 55 56 var _ = Describe("HaveField", func() { 57 var book Book 58 BeforeEach(func() { 59 book = Book{ 60 Title: "Les Miserables", 61 Author: person{ 62 FirstName: "Victor", 63 LastName: "Hugo", 64 DOB: time.Date(1802, 2, 26, 0, 0, 0, 0, time.UTC), 65 }, 66 Pages: 2783, 67 Sequel: &Book{ 68 Title: "Les Miserables 2", 69 }, 70 } 71 }) 72 73 DescribeTable("traversing the struct works", 74 func(field string, expected interface{}) { 75 Ω(book).Should(HaveField(field, expected)) 76 }, 77 Entry("Top-level field with default submatcher", "Title", "Les Miserables"), 78 Entry("Top-level field with custom submatcher", "Title", ContainSubstring("Les Mis")), 79 Entry("Nested field", "Author.FirstName", "Victor"), 80 Entry("Top-level method", "AuthorName()", "Victor Hugo"), 81 Entry("Nested method", "Author.DOB.Year()", BeNumerically("<", 1900)), 82 Entry("Traversing past a method", "AbbreviatedAuthor().FirstName", Equal("Vic")), 83 Entry("Traversing a pointer", "Sequel.Title", "Les Miserables 2"), 84 ) 85 86 DescribeTable("negation works", 87 func(field string, expected interface{}) { 88 Ω(book).ShouldNot(HaveField(field, expected)) 89 }, 90 Entry("Top-level field with default submatcher", "Title", "Les Mis"), 91 Entry("Top-level field with custom submatcher", "Title", ContainSubstring("Notre Dame")), 92 Entry("Nested field", "Author.FirstName", "Hugo"), 93 Entry("Top-level method", "AuthorName()", "Victor M. Hugo"), 94 Entry("Nested method", "Author.DOB.Year()", BeNumerically(">", 1900)), 95 Entry("Traversing a pointer", "Sequel.Title", "Les Mis 2"), 96 ) 97 98 Describe("when field lookup fails", func() { 99 It("errors appropriately", func() { 100 success, err := HaveField("BookName", "Les Miserables").Match(book) 101 Ω(success).Should(BeFalse()) 102 Ω(err.Error()).Should(ContainSubstring("HaveField could not find field named '%s' in struct:", "BookName")) 103 104 success, err = HaveField("BookName", "Les Miserables").Match(book) 105 Ω(success).Should(BeFalse()) 106 Ω(err.Error()).Should(ContainSubstring("HaveField could not find field named '%s' in struct:", "BookName")) 107 108 success, err = HaveField("AuthorName", "Victor Hugo").Match(book) 109 Ω(success).Should(BeFalse()) 110 Ω(err.Error()).Should(ContainSubstring("HaveField could not find field named '%s' in struct:", "AuthorName")) 111 112 success, err = HaveField("Title()", "Les Miserables").Match(book) 113 Ω(success).Should(BeFalse()) 114 Ω(err.Error()).Should(ContainSubstring("HaveField could not find method named '%s' in struct of type matchers_test.Book.", "Title()")) 115 116 success, err = HaveField("NoReturn()", "Les Miserables").Match(book) 117 Ω(success).Should(BeFalse()) 118 Ω(err.Error()).Should(ContainSubstring("HaveField found an invalid method named 'NoReturn()' in struct of type matchers_test.Book.\nMethods must take no arguments and return exactly one value.")) 119 120 success, err = HaveField("TooManyReturn()", "Les Miserables").Match(book) 121 Ω(success).Should(BeFalse()) 122 Ω(err.Error()).Should(ContainSubstring("HaveField found an invalid method named 'TooManyReturn()' in struct of type matchers_test.Book.\nMethods must take no arguments and return exactly one value.")) 123 124 success, err = HaveField("HasArg()", "Les Miserables").Match(book) 125 Ω(success).Should(BeFalse()) 126 Ω(err.Error()).Should(ContainSubstring("HaveField found an invalid method named 'HasArg()' in struct of type matchers_test.Book.\nMethods must take no arguments and return exactly one value.")) 127 128 success, err = HaveField("Pages.Count", 2783).Match(book) 129 Ω(success).Should(BeFalse()) 130 Ω(err.Error()).Should(Equal("HaveField encountered:\n <int>: 2783\nWhich is not a struct.")) 131 132 success, err = HaveField("Author.Abbreviation", "Vic").Match(book) 133 Ω(success).Should(BeFalse()) 134 Ω(err.Error()).Should(ContainSubstring("HaveField could not find field named '%s' in struct:", "Abbreviation")) 135 136 success, err = HaveField("Prequel.Title", "Les Miserables 0").Match(book) 137 Ω(success).Should(BeFalse()) 138 Ω(err.Error()).Should(ContainSubstring("HaveField encountered nil while dereferencing a pointer of type *matchers_test.Book.")) 139 }) 140 }) 141 142 Describe("Failure Messages", func() { 143 It("renders the underlying matcher failure", func() { 144 matcher := HaveField("Title", "Les Mis") 145 success, err := matcher.Match(book) 146 Ω(success).Should(BeFalse()) 147 Ω(err).ShouldNot(HaveOccurred()) 148 149 msg := matcher.FailureMessage(book) 150 Ω(msg).Should(Equal("Value for field 'Title' failed to satisfy matcher.\nExpected\n <string>: Les Miserables\nto equal\n <string>: Les Mis")) 151 152 matcher = HaveField("Title", "Les Miserables") 153 success, err = matcher.Match(book) 154 Ω(success).Should(BeTrue()) 155 Ω(err).ShouldNot(HaveOccurred()) 156 157 msg = matcher.NegatedFailureMessage(book) 158 Ω(msg).Should(Equal("Value for field 'Title' satisfied matcher, but should not have.\nExpected\n <string>: Les Miserables\nnot to equal\n <string>: Les Miserables")) 159 }) 160 }) 161 162 Describe("receiver lookup", func() { 163 DescribeTable("(pointer) receiver lookup works", 164 func(field string, expected interface{}) { 165 Ω(&book).Should(HaveField(field, expected)) 166 }, 167 Entry("non-pointer receiver", "ReceiverTitle()", "Les Miserables"), 168 Entry("pointer receiver", "PointerReceiverTitle()", "Les Miserables"), 169 ) 170 171 It("correctly fails", func() { 172 matcher := HaveField("ReceiverTitle()", "Les Miserables") 173 answer := struct{}{} 174 Ω(matcher.Match(answer)).Error().Should(MatchError( 175 "HaveField could not find method named 'ReceiverTitle()' in struct of type struct {}.")) 176 177 matcher = HaveField("PointerReceiverTitle()", "Les Miserables") 178 Ω(matcher.Match(book)).Error().Should(MatchError( 179 "HaveField could not find method named 'PointerReceiverTitle()' in struct of type matchers_test.Book.")) 180 }) 181 }) 182 183 })