type quotaNetworkFailure struct{ quotaSuccess }
func (quotaNetworkFailure) UpdateQuota() (bool, error) { return false, quota.ErrNetworkFailure }
func TestTransaction_QuotaUpdateNetworkFailure(t *testing.T) {
tx := &Transaction{
Auth: authSuccess{},
Payment: paymentSuccess{},
Storage: storageSuccess{},
Quota: quotaNetworkFailure{},
Notification: notificationSuccess{},
}
_, err := tx.Request("12345", "valid-token", "product123", 100.0, "USD", []byte("file data"))
if !errors.Is(quota.ErrNetworkFailure, err) {
t.Errorf("unexpected error: %v", err)
}
}
While still contrived, surely you agree that is much more understandable, not to mention a whole lot easier to write? Now we know why something might fail, and the reader learns what they should look for when handing a network failure.Yes, okay, you got me. That isn't the HTTP service anymore. But it should have never been. The transaction processing is not an HTTP concern. In fixing that, now you can make the HTTP end of things far less obtuse:
var errGeneralError = errors.New("general error")
type transactionFailure struct{}
func (transactionFailure) Request(id, token, productID string, amount float64 /* yikes */, currency string, file []byte) {
return false, errGeneralError
}
func TestBuy_Failure(t *testing.T) {
srv := httptest.NewServer(&Server{
Transaction: transactionFailure{},
})
defer srv.Close()
c := client.New(srv.URL)
_, err := c.Buy("product123")
if !errors.Is(transaction.ErrFailed, err) {
t.Errorf("unexpected error: %v", err)
}
}
But, this is still not a good example of input. Input doesn't matter when the network fails. Let's say we want to cover the case where the product ID being purchased doesn't exist instead: type transactionNoProduct struct{}
func (transactionNoProduct) Request(id, token, productID string, amount float64 /* yikes */, currency string, file []byte) (bool, error) {
if productID != "product123" {
return true, nil
}
return false, transaction.ErrProductNotFound
}
func TestBuy_ProductNotFound(t *testing.T) {
srv := httptest.NewServer(&Server{
Transaction: transactionNoProduct{},
})
defer srv.Close()
c := client.New(srv.URL)
_, err := c.Buy("product123")
if !errors.Is(transaction.ErrProductNotFound, err) {
t.Errorf("unexpected error: %v", err)
}
}
There is still plenty of room for improvement here that I will cut short as who cares for an HN comment, but man, I don't see how anyone can think that mockery monstrosity is preferable to... anything else. Where is it actually useful?