There are calls that are not supposed to be part of assertion but they are caught by the tests because they are not handled. For case like that, you can chain Maybe() to mark that expectation optional.
main_test.go
func TestCompleteOrder_MaybeMetrics(t *testing.T) {
ctx := context.Background()
tests := []struct {
name string
orderID string
sendReceipt bool
recordMetric bool
wantErr bool
}{
{name: "receipt_and_metric", orderID: "ord-1", sendReceipt: true, recordMetric: true, wantErr: false},
{name: "receipt_only", orderID: "ord-2", sendReceipt: true, recordMetric: false, wantErr: false},
{name: "metric_only", orderID: "ord-3", sendReceipt: false, recordMetric: true, wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sender := servicemock.NewReceiptSender(t)
metrics := servicemock.NewMetrics(t)
if tt.sendReceipt {
sender.On("SendReceipt", mock.Anything, tt.orderID).Return(nil).Once()
}
// Optional: metrics may or may not be recorded for a given request
metrics.On("Count", "orders.completed", 1).Return().Maybe()
err := CompleteOrder(ctx, sender, metrics, tt.orderID, tt.sendReceipt, tt.recordMetric)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
sender.AssertExpectations(t)
metrics.AssertExpectations(t)
})
}
}
main.go
//go:generate mockery --name=ReceiptSender --dir=. --output=./mocks --outpkg=servicemock --case=snake
//go:generate mockery --name=Metrics --dir=. --output=./mocks --outpkg=servicemock --case=snake
// ...
type ReceiptSender interface {
SendReceipt(ctx context.Context, orderID string) error
}
type Metrics interface {
Count(name string, value int)
}
func CompleteOrder(ctx context.Context, sender ReceiptSender, metrics Metrics, orderID string, sendReceipt bool, recordMetric bool) error {
if orderID == "" {
return fmt.Errorf("orderID must be non-empty")
}
if sendReceipt {
if err := sender.SendReceipt(ctx, orderID); err != nil {
return err
}
}
if recordMetric {
metrics.Count("orders.completed", 1)
}
return nil
}
Note: mocked object results are omitted.