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

Bug: Cannot return nil for generic type T, when T is an interface #513

Open
1 of 5 tasks
mwittie opened this issue Oct 20, 2022 · 3 comments
Open
1 of 5 tasks

Bug: Cannot return nil for generic type T, when T is an interface #513

mwittie opened this issue Oct 20, 2022 · 3 comments

Comments

@mwittie
Copy link

mwittie commented Oct 20, 2022

Description

For the interfaces

type Builder[T any] interface {
	Build() (T, error)
}

type Thing interface {
	DoStuff()
}

I'd like to mock Builder behavior as

thingBuilder := new(mocks.Builder[Thing])
thingBuilder.On("Build").Return(nil, errors.New("some error"))

But I get an error like:

panic: interface conversion: interface is nil, not Thing

Shouldn't mockery know that Thing is an interface and so Builder[Thing].Build() should be able to return (nil, error)?

Mockery Version

$ mockery --version
19 Oct 22 17:49 MDT INF Starting mockery dry-run=false version=v2.14.0
v2.14.0

Golang Version

$ go version
go version go1.19 linux/amd64

Installation Method

  • Binary Distribution
  • Docker
  • brew
  • go install
  • Other: [specify]

Expected Behavior

Can mock generic return types as nil when they are in fact interfaces.

Actual Behavior

Tests panic

@mwittie mwittie changed the title Cannot nil for generic type T, when T is an interface Bug: Cannot nil for generic type T, when T is an interface Oct 20, 2022
@mwittie mwittie changed the title Bug: Cannot nil for generic type T, when T is an interface Bug: Cannot use nil for generic type T, when T is an interface Oct 20, 2022
@mwittie mwittie changed the title Bug: Cannot use nil for generic type T, when T is an interface Bug: Cannot return nil for generic type T, when T is an interface Oct 20, 2022
@dlwyatt
Copy link
Contributor

dlwyatt commented Feb 5, 2023

This is a workaround to get the test working with the current version of mockery:

thingBuilder.On("Build").Return(func() Thing { return nil }, errors.New("some error"))

@dlwyatt
Copy link
Contributor

dlwyatt commented Feb 5, 2023

Possible fix: Generate the following code in all non-error cases, instead of the current isNillable check:

			v := ret.Get(0)
			if !reflect.ValueOf(&v).Elem().IsZero() && !reflect.ValueOf(v).IsZero() {
				r0 = v.(T)
			}

r0 is already the zero value for whatever its type is. The first call to IsZero (on ValueOf(&v).Elem()) covers the case where v is a nil, invalid interface (avoiding the panic in this issue), and the second call (on ValueOf(v) looks at whatever concrete type was passed into Return, assuming it's not the invalid nil interface. If both of those IsZero checks are false, then r0 needs to have a value assigned, and I think the type assertion v.(WhateverType) should always work.

This also requires an addition of g.addPackageImportWithName(ctx, "reflect", "reflect") up in NewGenerator

(The second check may not be strictly necessary, I was just trying to match the behavior of the code that mockery currently generates. It seems to work fine with just if !reflect.ValueOf(&v).Elem().IsZero() { even if the type is a pointer, map, slice, etc and the value is nil.)

@dlwyatt
Copy link
Contributor

dlwyatt commented Feb 6, 2023

Even just putting if v != nil in all cases (not bothering to check for nillable or using reflect) might work, tbh.

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

No branches or pull requests

2 participants