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

Decorations are not applied to parameters passed to group constructors #898

Closed
silverspace opened this issue Jul 19, 2022 · 1 comment
Closed

Comments

@silverspace
Copy link

Describe the bug
If I have a struct being injected as part of a group, and that struct contains an interface type with decorations, then the decorations for the interface type are ignored for the group constructor.

For example, suppose we have an interface Animal that has a production implementation of ProdBear, and a unit test decoration that returns MockBunny. When groups are not utilized, my app always populates bunny, since it applies the bunny decorator. However, when I create a Zoo with a group of exhibits, then bear is populated for the Zoo exhibits -- everywhere else bunny is populated.

To Reproduce

package main_test

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"go.uber.org/fx"
	"go.uber.org/fx/fxtest"
)

type Animal interface {
	Name() string
}

type ProdBear struct {
}

var _ Animal = (*ProdBear)(nil)

func NewProdBear() *ProdBear {
	return &ProdBear{}
}

func (p *ProdBear) Name() string {
	return "bear"
}

type MockBunny struct {
}

var _ Animal = (*MockBunny)(nil)

func (r *MockBunny) Name() string {
	return "bunny"
}

func NewMockBunny() *MockBunny {
	return &MockBunny{}
}

type Exhibit struct {
	Animal Animal
}

func NewExhibit(a Animal) *Exhibit {
	return &Exhibit{Animal: a}
}

type ZooParams struct {
	fx.In
	Exhibits []*Exhibit `group:"exhibits"`
}

type Zoo struct {
	Exhibits []*Exhibit
}

func NewZoo(p ZooParams) *Zoo {
	return &Zoo{Exhibits: p.Exhibits}
}

type Trainer struct {
	Animal Animal
}

func NewTrainer(a Animal) *Trainer {
	return &Trainer{Animal: a}
}

func TestFxApp(t *testing.T) {
	var env struct {
		fx.In
		Animal  Animal
		Trainer *Trainer
		Zoo     *Zoo
	}
	app := fxtest.New(t,
		fx.Provide(
			fx.Annotate(NewProdBear, fx.As(new(Animal))),
		),
		fx.Decorate(
			fx.Annotate(NewMockBunny, fx.As(new(Animal))),
		),
		fx.Provide(
			// Using `group` here results in NewExhibit() being passed a bear instead of a bunny.
			fx.Annotate(NewExhibit, fx.ResultTags(`group:"exhibits"`)),
		),
		fx.Provide(NewTrainer),
		fx.Provide(NewZoo),
		fx.Populate(&env),
	)
	defer app.RequireStart().RequireStop()

	assert.Equal(t, "bunny", env.Animal.Name())
	assert.Equal(t, "bunny", env.Trainer.Animal.Name())

	// This fails! Zoo contains a group of exhibits, and the NewExhibit()
	// constructor is only given the prod value, not the decorated value.
	//   expected: "bunny"
	//   actual  : "bear"
	assert.Equal(t, "bunny", env.Zoo.Exhibits[0].Animal.Name())

}

Expected behavior
I would expect the above unit test to pass.

Additional context
Tested with fx v1.17.1 and dig v1.14.1

@sywhang
Copy link
Contributor

sywhang commented Sep 22, 2022

Thanks for reporting this and sorry about the delayed response. This should be fixed with #941.

@sywhang sywhang closed this as completed Sep 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants