Skip to content
Permalink
Browse files

bot: add some more unit tests

  • Loading branch information...
fgrosse committed Mar 8, 2019
1 parent b026ede commit 873efcae67108a53d77009384f89176ccf3c8420
Showing with 117 additions and 7 deletions.
  1. +4 −3 CHANGELOG.md
  2. +1 −1 bot.go
  3. +100 −2 bot_test.go
  4. +1 −0 go.sum
  5. +11 −1 testing.go
@@ -6,11 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
### Added
- Add new functions to `TestBot` type to ease bot start and shutdown in unit tests
- Add new function to `TestBot` to emit events synchronously
- Add `TestBot.Start()` and `TestBot.Stop()`to ease synchronously starting and stopping bot in unit tests
- Add `TestBot.EmitSync(…)` to emit events synchronously in unit tests
- Add `TestBot.GetOutput()` to easily get adapter output in unit tests

### Changed
- Remove context argument from `NewTest(…)` function
- Remove obsolete context argument from `NewTest(…)` function

## [v0.1.0] - 2019-03-03

2 bot.go
@@ -211,7 +211,7 @@ func (b *Bot) RespondRegex(expr string, fun func(Message) error) {

regex, err := regexp.Compile(expr)
if err != nil {
b.Logger.Error("Failed to add Response handler", zap.Error(err))
b.Brain.registrationErrs = append(b.Brain.registrationErrs, errors.Wrap(err, "failed to add Response handler"))
return
}

@@ -8,11 +8,13 @@ import (

"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest/observer"
)

// TODO: test Bot.Say

func TestBot_Run(t *testing.T) {
b := NewTest(t)

@@ -137,6 +139,7 @@ func TestBot_RespondRegex(t *testing.T) {
"NAME IS Joe": {"Joe"}, // simple case, case insensitive
"Hello, my name is Joe": {"Joe"}, // match on substrings
"My name is Joe and what is yours?": nil, // respect end of input anchor
"": nil, // should not match but also not panic
}

for input, matches := range cases {
@@ -163,6 +166,42 @@ func TestBot_RespondRegex(t *testing.T) {
}
}

func TestBot_RespondRegex_Empty(t *testing.T) {
b := NewTest(t)
b.RespondRegex("", func(msg Message) error {
t.Error("should never match")
return nil
})

b.Start()
defer b.Stop()

cases := []string{
"",
" ",
"\n",
"\t",
"foobar",
"foo bar",
}

for _, input := range cases {
b.EmitSync(t, ReceiveMessageEvent{Text: input})
}
}

func TestBot_RespondRegex_Invalid(t *testing.T) {
b := NewTest(t)
b.RespondRegex("this is not a [valid regular expression", func(msg Message) error {
t.Error("should never match")
return nil
})

err := b.Run()
require.EqualError(t, err, "invalid event handlers: failed to add Response handler: "+
"error parsing regexp: missing closing ]: `[valid regular expression`")
}

func TestBot_CloseAdapter(t *testing.T) {
input := &testCloser{Reader: new(bytes.Buffer)}
output := new(bytes.Buffer)
@@ -211,6 +250,47 @@ func TestBot_RegistrationErrors(t *testing.T) {
assert.Regexp(t, "event handler needs one or two arguments", err.Error())
}

// TestBot_Logger simply tests that the zap logger configuration in newLogger()
// doesn't panic.
func TestBot_Logger(t *testing.T) {
newLogger()
}

func TestBot_Say(t *testing.T) {
a := new(MockAdapter)
b := NewTest(t)
b.Adapter = a

a.On("Send", "Hello world", "foo").Return(nil)
b.Say("foo", "Hello world")

a.On("Send", "Hello world: the answer is 42", "bar").Return(nil)
b.Say("bar", "Hello %s: the answer is %d", "world", 42)

a.AssertExpectations(t)
}

func TestBot_Say_Error(t *testing.T) {
obs, logs := observer.New(zap.DebugLevel)
logger := zap.New(obs)

a := new(MockAdapter)
b := NewTest(t)
b.Adapter = a
b.Logger = logger

adapterErr := errors.New("watch your language")
a.On("Send", "damn it", "baz").Return(adapterErr)
b.Say("baz", "damn it")

assert.Equal(t, []observer.LoggedEntry{{
Entry: zapcore.Entry{Level: zap.ErrorLevel, Message: "Failed to send message"},
Context: []zapcore.Field{zap.Error(adapterErr)},
}}, logs.AllUntimed())

a.AssertExpectations(t)
}

type testCloser struct {
Closed bool
io.Reader
@@ -229,3 +309,21 @@ func wait(t *testing.T, c chan bool) {
t.Fatal("timeout")
}
}

type MockAdapter struct {
mock.Mock
}

func (a *MockAdapter) Register(r EventRegistry) {
a.Called(r)
}

func (a *MockAdapter) Send(text, channel string) error {
args := a.Called(text, channel)
return args.Error(0)
}

func (a *MockAdapter) Close() error {
args := a.Called()
return args.Error(0)
}
1 go.sum
@@ -4,6 +4,7 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -108,6 +108,16 @@ func (b *TestBot) Stop() {
b.stop()
err := <-b.runErr
if err != nil {
b.T.Errorf("Bot.Run() returned an error: %v")
b.T.Errorf("Bot.Run() returned an error: %v", err)
}
}

// GetOutput reads all output from b.Output and returns it as string.
func (b *TestBot) GetOutput() string {
out, err := ioutil.ReadAll(b.Output)
if err != nil {
b.T.Errorf("Failed to get TestBot.Output: %v", err)
}

return string(out)
}

0 comments on commit 873efca

Please sign in to comment.
You can’t perform that action at this time.