Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split Check and Value into multiple types. #10

Open
smyrman opened this issue Jun 9, 2020 · 1 comment
Open

Split Check and Value into multiple types. #10

smyrman opened this issue Jun 9, 2020 · 1 comment
Labels
enhancement New feature or request

Comments

@smyrman
Copy link
Contributor

smyrman commented Jun 9, 2020

The helper function name-space in ValueFunc is getting quite crowded. I am wondering if it would make sense to actually split it up into several ValueFunc types with co-responding Check types and errors. E.g. instead of writing:

subtest.Value(err).IsError(target)

Write:

subtest.Error(err).Is(target)

This is perhaps not where it's most needed, but consider the difference between:

subtest.Value("foobar").Contains("foo")
subtest.Value([]string{"foobar"}).Contains("foo")

Contain may support both or only one of this cases; It's not really clear from the code, and we won't get a compile error if we write the an unsupported variant. We could make it clearer by lengthening the names, e.g. call it StringContains and SliceContains, or maybe even ArrayContains. Or we can support both cases in one, but then the check becomes overloaded, as it does more than one thing, depending on the input type. There is already some tendency of check overloading.

This however, is quite clear:

subtest.String("foobar").Contains("foo")
subtest.Slice([]string{"foobar"}).Contains("foo")

Splitting into multiple value types allow a separate namespace for helpers, allowing names to become short and precise. As for the Check interface changes, Middleware etc. I think I can pull it off, and maybe at the same time introduce some type-safety for checks.

Quick draft of the solution

What I am suggesting is to introduce multiple Check and Value Func types. However, all XCheck types in the subtest package will also implement Check; this way they can easily be used in composite Checks such as Schema, AllOf or AnyOf. Converters and CheckMiddleware will help "convert" checks and values.

Check types

type Check interface{
	Check(ValueFunc) error
}

CheckFunc func(got interface{}) error
has .Check(ValueFunc) error

type ErrorCheck interface{
	CheckError(ErrorFunc) error
}

ErrorCheckFunc func(got error) error
has . CheckError(ErrorFunc) error
has .Check(ValueFunc) error

type NumericCheck interface{
	CheckNumber(NumericFunc) error
}

NumericCheckFunc func(got float64) error
has .CheckNumber(ErrorFunc) error
has .Check(ValueFunc) error

type TimeCheck interface{
	CheckTime(TimeFunc) error
}

TimeCheckFunc func(got time.Time) error
has .CheckTime(TimeFunc) error
has. Check(ValueFunc) error


type StringCheck interface{
	CheckString(TimeFunc) error
}

StringCheckFunc func(got time.Time) error
has .CheckString(StringFunc) error
has. Check(ValueFunc) error

Middleware



Check Middleware is probably mostly OK to work like this...

OnNumeric(NumericCheck) Check
OnLen(NumericCheck)Check
OnCap(NumericCheck)Check
OnIndex(int, Check) Check
OnKey(interface{}, Check) Check
OnError(ErrorCheck) Check
OnTime(TimeCheck) Check


This has some mentionable trade-offs. We can't by accident to OnLen(nonNumericChec); we would get a compile-time error. In some cases this could be nice, but it also means that we can't compose numeric checks. I.e.

OnLen(AllOf{subtest.GreaterThan(5), subtest.LessThan(10)}) // Compile-time error
AllOf{OnLen(subtest.GreaterThan(5)), OnLen(subtest.LessThan(10))} // OK

Value types

And finally, value types with fewer helpers on each...


func Value(interface{}) ValueFunc

type ValueFunc func() (value, error)
has .Test(Check) func(t *testing.T)
// Helpers
has .DeepEqual(interface{}) func(t *testing.T)
has .NotDeepEqual(interface{}) func(t *testing.T)
has .ReflectNil() func(t *testing.T)
has .NotReflectNil() func(t *testing.T)
// Converters
has .AsNumeric() NumericFunc
has .AsError() ErrorFunc
has .AsTime() TimeFunc
has .AsSlice() SliceFunc
etc.

func Error(error) ErrorFunc

type ErrorFunc func() (error, error)
has .Test(ErrorCheck) func (t *testing.T)// Helpers
has .IsError() bool
has .IsNil() bool
has .Is(target error) bool
has .As(target interface{}) bool
// Converters
.AsValue() ValueFunc
.AsString() StringFunc


// We could optionally be explicit about the source type when initializing
// a numeric value
func Float64(float64) NumericFunc
func Int64(int64) NumericFunc
func ParseNumeric(s string) 

Numeric(interface{}) NumericFunc
type NumericFunc func() (float64, error)
has .Test(NumericCheck) func(t *testing.T)
// Helpers.
has .Equal(float64) bool
has .NotEqual(float64) bool
has .GreaterThan(float64) bool
has .GreaterThanOrEqual(float64) bool
has .LessThan(float64) bool
has .LessThanOrEqual(float64) bool
// Converters.
has .AsValue() ValueFunc

type StringFunc
has .Test(StringCheck) func (t *testing.T)
// TODO: Check the `strings` package and add some useful helpers.

type BytesFunc
has .Test(BytestCheck) func (t *testing.T)
// TODO: Check the `bytes` package and add some useful functions.
 
// SliceFunc does not hace their own CheckFunc type.
type SliceFunc func() (interface{}, error)
 has .Test(Check) func(t *testing.T)
// Helopers
has .IsNil()
has .IsNotNil()
has .Contains(interface{}) func(t *testing.T)
has .MatchBag(map[interface{}]int) func(t *testing.T)
has .NotMatchBag(map[interface{}]int) func(t *testing.T)
has .MatchSet(map[interface{}) func(t *testing.T)
has .NotMatchSet(map[interface{}) func(t *testing.T)
// Converters
has .AsValue() ValueFunc
@smyrman smyrman added the enhancement New feature or request label Jun 9, 2020
@smyrman
Copy link
Contributor Author

smyrman commented Oct 26, 2021

On hold until the arrival of generics in Go 1.18, in which case we can update this module based on the experiments in https://github.com/smyrman/subx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant