skip to content
Alvin Lucillo

Optional expectations

/ 2 min read

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.