Skip to content

Commit 9ec4913

Browse files
committed
feat: add Regex matcher for strings
1 parent 892b665 commit 9ec4913

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

gomock/matchers.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package gomock
1717
import (
1818
"fmt"
1919
"reflect"
20+
"regexp"
2021
"strings"
2122
)
2223

@@ -168,6 +169,26 @@ func (n notMatcher) String() string {
168169
return "not(" + n.m.String() + ")"
169170
}
170171

172+
type regexMatcher struct {
173+
regex string
174+
}
175+
176+
func (m regexMatcher) Matches(x any) bool {
177+
str, ok := x.(string)
178+
if !ok {
179+
return false
180+
}
181+
match, err := regexp.MatchString(m.regex, str)
182+
if !match || err != nil {
183+
return false
184+
}
185+
return true
186+
}
187+
188+
func (m regexMatcher) String() string {
189+
return "matching regex " + m.regex
190+
}
191+
171192
type assignableToTypeOfMatcher struct {
172193
targetType reflect.Type
173194
}
@@ -382,6 +403,17 @@ func Not(x any) Matcher {
382403
return notMatcher{Eq(x)}
383404
}
384405

406+
// Regex checks whether a string parameter matches the associated regex.
407+
//
408+
// Example usage:
409+
//
410+
// Regex("[0-9]{2}:[0-9]{2}").Matches("23:02") // returns true
411+
// Regex("[0-9]{2}:[0-9]{2}").Matches("hello world") // returns false
412+
// Regex("[0-9]{2}").Matches(21) // returns false as it's not a string
413+
func Regex(regexStr string) Matcher {
414+
return regexMatcher{regex: regexStr}
415+
}
416+
385417
// AssignableToTypeOf is a Matcher that matches if the parameter to the mock
386418
// function is assignable to the type of the parameter to this function.
387419
//

gomock/matchers_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func TestMatchers(t *testing.T) {
4747
[]e{nil, (error)(nil), (chan bool)(nil), (*int)(nil)},
4848
[]e{"", 0, make(chan bool), errors.New("err"), new(int)}},
4949
{"test Not", gomock.Not(gomock.Eq(4)), []e{3, "blah", nil, int64(4)}, []e{4}},
50+
{"test Regex", gomock.Regex("[0-9]{2}:[0-9]{2}"), []e{"23:02", "[23:02]: Hello world"}, []e{4, "23-02", "hello world", true}},
5051
{"test All", gomock.All(gomock.Any(), gomock.Eq(4)), []e{4}, []e{3, "blah", nil, int64(4)}},
5152
{"test Len", gomock.Len(2),
5253
[]e{[]int{1, 2}, "ab", map[string]int{"a": 0, "b": 1}, [2]string{"a", "b"}},
@@ -92,6 +93,52 @@ func TestNotMatcher(t *testing.T) {
9293
}
9394
}
9495

96+
// A more thorough test of regexMatcher
97+
func TestRegexMatcher(t *testing.T) {
98+
tests := []struct {
99+
name string
100+
regex string
101+
input any
102+
wantMatch bool
103+
wantStringResponse string
104+
}{
105+
{
106+
name: "match for whole num regex with start and end position matching",
107+
regex: "^\\d+$",
108+
input: "2302",
109+
wantMatch: true,
110+
wantStringResponse: "matching regex ^\\d+$",
111+
},
112+
{
113+
name: "match for valid regex with start and end position matching on longer string",
114+
regex: "^[0-9]{2}:[0-9]{2}$",
115+
input: "[23:02]: Hello world",
116+
wantMatch: false,
117+
wantStringResponse: "matching regex ^[0-9]{2}:[0-9]{2}$",
118+
},
119+
{
120+
name: "match for invalid regex",
121+
regex: "^[0-9{2}:[0-9]{2}$",
122+
input: "23:02",
123+
wantMatch: false,
124+
wantStringResponse: "matching regex ^[0-9{2}:[0-9]{2}$",
125+
},
126+
}
127+
128+
for _, tt := range tests {
129+
t.Run(tt.name, func(t *testing.T) {
130+
matcher := gomock.Regex(tt.regex)
131+
132+
if got := matcher.Matches(tt.input); got != tt.wantMatch {
133+
t.Errorf("got = %v, wantMatch = %v", got, tt.wantMatch)
134+
}
135+
if gotStr := matcher.String(); gotStr != tt.wantStringResponse {
136+
t.Errorf("got string = %v, want string = %v", gotStr, tt.wantStringResponse)
137+
}
138+
})
139+
}
140+
}
141+
95142
type Dog struct {
96143
Breed, Name string
97144
}

0 commit comments

Comments
 (0)