peteches_
peteches_

Reputation: 83

Testing Golang function containing call to sql.Open connection without a DB

So I'm just getting to grips with Golang. I'm writing an application for funsies just to understand stuff and get my head around it.

I have a whole bunch of functions that will interact with a DB where I pass in *SQL.DB for the function to use. I can test those easily enough using a mocked interface from sqlmock.

No problem there.

I'm now writing the Initialisation function for the application which will initiate the DB connection which will be attached to a struct and from there passed into utility functions.

However, I am struggling to find a way to easily test that connection without having the hassle of setting up an actual database.

So I guessing that I have probably either badly structured my app, or I've missed something, potentially pretty obvious.

So here is some example code to illustrate my predicament.

util.go

package main
import (
    "log"
    "database/sql"
    "github.com/go-sql-driver/mysql"
)

func DoDBStuff(db *sql.DB) {
    rows, err := db.Query("SELECT column1, column2 FROM example")
    if err != nil {
       log.Error(err)
    }
    // do stuff with rows
}

util_test.go

package main

import (
    "testing"
    "github.com/DATA-DOG/go-sqlmock"
)

func TestDoDBStuff(t *testing.T) {
    db, mock, err := sqlmock.New()
    if err != nil {
        t.Fatalf("An error '%s' was not expected when opening a stub database connection", err)
    }
    defer db.Close()

    rows := sqlmock.NewRows([]string{"col1", "col2"})
    rows.AddRow("val1", "val2")
    rows.AddRow("val3", "val4")

    mock.ExpectQuery("^SELECT column1, column2 from example$").WillReturnRows(rows)

    DoDBStuff(db)

    if err := mock.ExpectationsWereMet(); err != nil {
        t.Errorf("there were unfulfilled expectations: %s", err)
    }

}

That all works fine, I can test my DB queries.

However Now I want to test Initialising the App.

package main

import (
    "database/sql"
    "github.com/go-sql-driver/mysql"
)

type App {
    DB *sql.DB
    // some other data structures
}

func (a *App) InitApp(connectionString string) {
    a.DB = sql.Open("mysql", connectionString)
    // other init stuff
}

But as I can't pass in the SQL I don't think it can be mocked, certainly not easily. So I'm struggling a bit on how to move forward.

I am intending for this to sit behind a rest API, so on startup, the app will need to initialize before being able to process any requests.

Ideally, I'd like to be able to test the REST interface without having to set up a database, delay testing with real data until I can feed the code into a Dev environment.

So really I want to know:

Is what I'm intending possible? Is there a better approach?

If not what am I missing? Poor test design or poor code set up?

Edit:

Folling @peter's comment I just want to clarify.

I want to test the functionality of the InitDB() function but with the sql.Open call I would need to have a Database for it to connect to, If I don't then I the call would fail and I could not effectively test the function.

Upvotes: 7

Views: 3321

Answers (1)

chabad360
chabad360

Reputation: 640

There is Dockertest which creates a Docker container running whatever you want (i.e. MySQL), and you can test against that. It's designed for being able to do proper integration testing, so it should be able to do what you want (you wont need sqlmock anymore too).

Upvotes: 4

Related Questions