dqsxsmi3704 2017-10-22 23:30
浏览 50


TLDR: What's the best way of unit-testing a function that uses a database as its datasource but that the datasource does not have to be a database (that is, the function isn't aware of where the data is coming from. e.g. it can be an object, etc.)?


I'm trying to find out what's the best practice for unit-testing functions that use a database as its datasource - which is passed through its parameter - but that the datasource does not have to be a database (e.g. it can be an object, etc.). The reason I add the clause, "that the datasource does not have to be a database," is because I don't want to use a database during unit-tests. So, how do I write a function that is database-agnostic?

One possible approach is to provide an "env" parameter that holds the datasource, like so (GoLang):

type Env struct {
    DataSource  interface{}

func FunctionToTest(env Env) {
    switch et := (env.DataSource).(type) {
    case UserDatasource:
        userSource := (env.DataSource).(UserDatasource)
        user := userSource.getUser()
        // Throw error

func FunctionToTest2(env Env) {
    switch et := (env.DataSource).(type) {
    case CredentialsDatasource:
        credentialSource := (env.DataSource).(CredentialsDatasource)
        password := credentialSource.getPassword()
        // Throw error

The issue with this is that it seems "hacky" and it feels like there exists a better solution. The reason I want to learn how to do this is so that I can write unit-tests that mock the database.

I appreciate any and all input.

Thanks in advance!

  • 写回答

1条回答 默认 最新

  • dsgk0386 2017-10-23 02:12

    You would want to use an interface here. A common pattern used to abstract away where the data comes from is the repository pattern. Here is an article where you can read about the repository pattern and other tips for developing well-written Go applications.

    package user
    type User struct {
        ID int64
        Name string
    type Repository interface {
        Find(id int64) (*User, error)
        Store(u *User) error
    func ChangeUserName(id int64, name string, r Repository) error {
        u, err := r.Find(id)
        if err != nil {
            return err
        u.Name = name
        err = r.Store(u)
        return err

    Now you are able to pass in any struct with the methods matching your user.Repository interface. For example:

    package mysql
    type DB struct {
    func New(db *sql.DB) *DB {
        return DB{db}
    func (d *DB) Find(id int64) (*user.User, error) {
        // mysql stuff to find user
    func (d *DB) Store(u *user.User) error {
        // mysql stuff to store user

    then ...

    package main
    func main() {
        // ...
        // conn := code to open *sql.DB connection
        db := mysql.New(conn)
        err := user.ChangeUserName(1, 'bob', db)
        // ...

    You can now also use a mock to test your function.

    package mocks
    type UserRepository struct {
        Users []*user.User
        ShouldError bool
    func (r *UserRepository) Find(id int64) (*user.User, error) {
        if r.ShouldError {
            return nil, errors.New("")
        for _, u := range r.Users {
            if u.ID == id {
                return u
        return errors.New("user not found")
    func (r *UserRepository) Store(u *user.User) error {
        if r.ShouldError {
            return errors.New("")
        r.Users = append(r.Users, u)
        return nil

    then to test...

    func Test_ChangeUserName_Stores_Changed_User(t *testing.T) {
        u := &user.User{ID: 1, Name: 'Bob'}
        r := mocks.UserRepository{Users: []*user.User{u}}
        err := user.ChangeUserName(1, 'Fred', r)
        // ...
    本回答被题主选为最佳回答 , 对您是否有帮助呢?



    • ¥20 powerbulider 导入excel文件,显示不完整
    • ¥30 单片机实验pc软件完全,可以私我商议
    • ¥20 #关于multisim绘图遇到的问题
    • ¥15 用keil调试程序保证结果进行led相关闪烁
    • ¥15 paddle训练自己的数据loss降不下去
    • ¥20 用matlab的pdetool解决以下三个问题
    • ¥15 单个福来轮的平衡与侧向滑动是如何做到的?
    • ¥15 嵌入式Linux固件,能直接告诉我crc32校验的区域在哪不,内核的校验我已经找到了,uboot没有
    • ¥20 h3c静态路要求有详细过程
    • ¥15 调制识别中输入为时频图,星座图,眼图等