#Testing in Go!

10 messages · Page 1 of 1 (latest)

fair fox
#

Hey there!

I'm transitioning from Python and Ruby, where we often use monkeypatching to simplify testing. I've noticed that this approach doesn't really apply in Go, so I'm keen to learn best practices for testing here.

I've got a basic API controller that handles new user registration—it processes the payload and then calls a repository to save the data in a database.

What's the best way to test this without involving the actual database? Should I consider using dependency injection to pass in a Repository instance, so I can use a mock for testing?

Thanks in advance for your advice!

type UserCredentials struct {
    Email    string `json:"email"`
    Password string `json:"password"`
}

func RegisterNewUserController(c *gin.Context) {
    var user UserCredentials

    if err := c.ShouldBindJSON(&user); err != nil {
        fmt.Print(err)
        c.JSON(400, gin.H{"message": "bad request"})
        return
    }

    new_user := models.User{Email: user.Email, Password: user.Password}

    repository, err := repositories.NewUserRepository()
    if err != nil {
        fmt.Print(err)
        c.JSON(500, gin.H{"message": "Sorry dude, we failed you..."})
        return
    }
    user_id, err := repository.SaveUser(&new_user)
    if err != nil {
        fmt.Print(err)
        c.JSON(500, gin.H{"message": "Sorry dude, we failed you..."})
        return
    }

    c.JSON(http.StatusOK, gin.H{"ID": user_id})
}
#

Sorry for the lack of comments btw. I was hitting the character limit

heady trail
#

@fair fox questions: is repositories.NewUserRepository supposedly some sort of database connection?
shouldnt you refrain from creating a new full db connection for every request?

#

generally from what i see, it's better to create a struct which the RegisterNewUserController function is bound to

#

this way you can store persistent things in the struct(ex a db connection) which get reused for all request

#

now that perfectly segues into testing
In prod env
the main code would initialize a full sql db connection, and pass it to the struct which holds the handler methods
in testing env
the test code could initialize a mock, or a immem provider to the controller struct

#

so yes you would use DI to pass a mock repository if you dont want to do real db

fair fox
#

@heady trail thank you so much for this!

I've got a quick question about testing. If I decide to pass a mock database instance to the controller for testing purposes, would I also need to mock the repository layer? I know this might be dipping my toes into testing philosophy, but when unit testing this particular controller, should I focus on mocking all underlying logic, or is it enough to just validate the controller's functionality, like HTTP statuses and responses?

Thanks in advance for your guidance! 😊

heady trail
# fair fox <@116909055838126080> thank you so much for this! I've got a quick question ab...

i am going to assume
controller->repository->database
i assume repository is the thing that interfaces with the database and provide queries/update/insert operations to the controller

in my opinion:
you could just mock the repository since that's the "closest level to the testing subject"
the mocked repository would not need to connect with a database because it's a mock object, it's behaving in the way you set it up to
the other idea is to implement a repository provider for the purpose of testing, and make it based in memory (stored in maps and slices) (not so applicable to impliment if your thing relies on SQL logic(unique, fkeys etc))

#

with that said i dont see any demerit mocking the db instead
though some might say you want to isolate the subject you are testing, and remove any unrelated things