Search code examples
unit-testinggodependency-injectionframeworks

How to create unit test dependency injection with Golang


For example, I want to create a user API with a dependency injection structure like this

func Bootstrap(config *BootstrapConfig) {
// setup repositories
userRepository := repository.NewUserRepository(config.Log)
// setup producer
userProducer := messaging.NewUserProducer(config.Producer, config.Log)
// setup use cases
userUseCase := usecase.NewUserUseCase(config.DB, config.Log, config.Validate, userRepository, userProducer)
// setup controller
userController := http.NewUserController(userUseCase, config.Log)
routeConfig := route.RouteConfig{
    App:               config.App,
    UserController:    userController,
}
routeConfig.Setup()
}

Then, I want to create an unit test for the user creation API, but I don't want real interaction with the database or mocking. Since the userUseCase requires a repository, it means we'll create a mock userRepository for the SaveUserToDB function.

Is it the right way to use a method like this?

mockRepo.On("SaveUserToDB", mock.Anything, mock.AnythingOfType("*repository.User")).Return(nil)

Solution

  • Dependency injection requires the dependencies to actually be injected. Bootstrap is creating its dependencies, so it can be argued this isn't dependency injection. If you use Bootstrap in your test, then this is definitely not dependency injection.

    So you need to change func Bootstrap(config *BootstrapConfig) to at least func Bootstrap(config *BootstrapConfig, userRepository *repository.User).

    Then, in your test, you'd pass mockRepo to Bootstrap.

    func Bootstrap(config *BootstrapConfig, userRepository *repository.User) {
        // setup producer
        userProducer := messaging.NewUserProducer(config.Producer, config.Log)
        // setup use cases
        userUseCase := usecase.NewUserUseCase(config.DB, config.Log, config.Validate, userRepository, userProducer)
        // setup controller
        userController := http.NewUserController(userUseCase, config.Log)
        routeConfig := route.RouteConfig{
            App:               config.App,
            UserController:    userController,
        }
        routeConfig.Setup()
    }
    
    func TestFoo(t *testing.T) {
        mockRepo := mocks.NewUserRepository(config.Log)
        Bootstrap(config, mockRepo)
        mockRepo.On("SaveUserToDB", mock.Anything).Return(nil)
        …
    }
    

    Bootstrap should not create any dependency, so you should apply this to userProducer, userUseCase, etc.