diff --git a/assert.go b/assert.go index 5c652c1c..68faf619 100644 --- a/assert.go +++ b/assert.go @@ -10,6 +10,9 @@ import ( // AssertPredicate is a custom predicate based on the items. type AssertPredicate func(items []interface{}) error +// ErrorPredicate is a custom predicate based on the errors. +type ErrorPredicate func(errors []error) error + // RxAssert lists the Observable assertions. type RxAssert interface { apply(*rxAssert) @@ -24,27 +27,30 @@ type RxAssert interface { itemToBeChecked() (bool, interface{}) noItemToBeChecked() (bool, interface{}) customPredicatesToBeChecked() (bool, []AssertPredicate) + customErrorsPredicatesToBeChecked() (bool, []ErrorPredicate) } type rxAssert struct { - f func(*rxAssert) - checkHasItems bool - checkHasNoItems bool - checkHasSomeItems bool - items []interface{} - checkHasItemsNoOrder bool - itemsNoOrder []interface{} - checkHasRaisedError bool - err error - checkHasRaisedErrors bool - errs []error - checkHasRaisedAnError bool - checkHasNotRaisedError bool - checkHasItem bool - item interface{} - checkHasNoItem bool - checkHasCustomPredicate bool - customPredicates []AssertPredicate + f func(*rxAssert) + checkHasItems bool + checkHasNoItems bool + checkHasSomeItems bool + items []interface{} + checkHasItemsNoOrder bool + itemsNoOrder []interface{} + checkHasRaisedError bool + err error + checkHasRaisedErrors bool + errs []error + checkHasRaisedAnError bool + checkHasNotRaisedError bool + checkHasItem bool + item interface{} + checkHasNoItem bool + checkHasCustomPredicate bool + customPredicates []AssertPredicate + checkHasCustomErrorPredicates bool + customErrorPredicates []ErrorPredicate } func (ass *rxAssert) apply(do *rxAssert) { @@ -95,6 +101,10 @@ func (ass *rxAssert) customPredicatesToBeChecked() (bool, []AssertPredicate) { return ass.checkHasCustomPredicate, ass.customPredicates } +func (ass *rxAssert) customErrorsPredicatesToBeChecked() (bool, []ErrorPredicate) { + return ass.checkHasCustomErrorPredicates, ass.customErrorPredicates +} + func newAssertion(f func(*rxAssert)) *rxAssert { return &rxAssert{ f: f, @@ -180,6 +190,17 @@ func CustomPredicate(predicate AssertPredicate) RxAssert { }) } +// CustomErrorPredicate checks a custom error predicate. +func CustomErrorPredicate(predicate ErrorPredicate) RxAssert { + return newAssertion(func(a *rxAssert) { + if !a.checkHasCustomPredicate { + a.checkHasCustomErrorPredicates = true + a.customErrorPredicates = make([]ErrorPredicate, 0) + } + a.customErrorPredicates = append(a.customErrorPredicates, predicate) + }) +} + func parseAssertions(assertions ...RxAssert) RxAssert { ass := new(rxAssert) for _, assertion := range assertions { @@ -260,6 +281,15 @@ loop: assert.Equal(t, expectedError, errs[0]) } } + if checkHasRaisedError, predicates := ass.customErrorsPredicatesToBeChecked(); checkHasRaisedError { + for _, predicate := range predicates { + err := predicate(errs) + if err != nil { + assert.FailNow(t, "custom error assertion failed %v", err) + } + } + } + if checkHasRaisedErrors, expectedErrors := ass.raisedErrorsToBeChecked(); checkHasRaisedErrors { assert.Equal(t, expectedErrors, errs) } diff --git a/doc/assert.md b/doc/assert.md index 868fa815..4905d8a0 100644 --- a/doc/assert.md +++ b/doc/assert.md @@ -114,4 +114,22 @@ rxgo.Assert(ctx, t, observable, rxgo.CustomPredicate(func(items []interface{}) e } return nil })) +``` + +### CustomErrorPredicate + +Implement a custom error predicate + +```go +errorPredicate := func(observedErrors []error) error { + if len(observedErrors) != 1 { + return errors.New("expecting one error only") + } + var expected *json.SyntaxError + if errors.As(observedErrors[0], &expected) { + return nil + } + return fmt.Errorf("Expected error %v but found %v", json.SyntaxError{}, observedErrors[0]) +} +rxgo.Assert(ctx, t, obs, rxgo.CustomErrorPredicate(errorPredicate)) ``` \ No newline at end of file diff --git a/observable_operator_test.go b/observable_operator_test.go index 229d7a82..6f90192d 100644 --- a/observable_operator_test.go +++ b/observable_operator_test.go @@ -2239,6 +2239,27 @@ func Test_Observable_Unmarshal_Error(t *testing.T) { Assert(ctx, t, obs, HasAnError()) } +func Test_Observable_Unmarshal_CustomErrorError(t *testing.T) { + defer goleak.VerifyNone(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + obs := testObservable(ctx, []byte(`{"id":1`), []byte(`{"id":2}`)).Unmarshal(json.Unmarshal, + func() interface{} { + return &testStruct{} + }) + errorPredicate := func(observedErrors []error) error { + if len(observedErrors) != 1 { + return errors.New("expecting one error only") + } + var expected *json.SyntaxError + if errors.As(observedErrors[0], &expected) { + return nil + } + return fmt.Errorf("Expected error %v but found %v", json.SyntaxError{}, observedErrors[0]) + } + Assert(ctx, t, obs, CustomErrorPredicate(errorPredicate)) +} + func Test_Observable_Unmarshal_Parallel(t *testing.T) { defer goleak.VerifyNone(t) ctx, cancel := context.WithCancel(context.Background())