Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Testgetriebene Entwicklung zur Überprüfung von Datenbankabfragen beteiligter Methoden

Ich hatte vor nicht allzu langer Zeit eine ähnliche Frage, als ich einige meiner eigenen Tests umgestaltete, und es gibt ein paar Möglichkeiten, wie Sie dies tun können:

a) Geben Sie einen exportierten Typ und ein Open an oder Connect Funktion, die es zurückgibt - z. B.

type DB struct {
    db *sql.DB
}

// Using http://jmoiron.github.io/sqlx/ for this example, but
// it has the same interface as database/sql
func Open(opts *Options) (*DB, error) {
    db, err := sqlx.Connect(opts.Driver, fmt.Sprintf("host=%s user=%s dbname=%s sslmode=%s", opts.Host, opts.User, opts.Name, opts.SSL))
    if err != nil {
        return nil, err
    }

    return &DB{db}, nil
}

... und dann jeder von Ihnen Tests, Setup- und Teardown-Funktionen schreiben, die eine Instanz von *DB zurückgeben auf denen Sie Ihre Datenbankfunktionen definieren (als Methoden - z. B. func (db *DB) GetUser(user *User) (bool, error) ):

// Setup the test environment.
func setup() (*DB, error) {
    err := withTestDB()
    if err != nil {
        return nil, err
    }

    // testOptions is a global in this case, but you could easily
    // create one per-test
    db, err := Open(testOptions)
    if err != nil {
        return nil, err
    }

    // Loads our test schema
    db.MustLoad()
    return db, nil
}

// Create our test database.
func withTestDB() error {
    db, err := open()
    if err != nil {
        return err
    }
    defer db.Close()

    _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s;", testOptions.Name))
    if err != nil {
        return err
    }

    return nil
}

Beachten Sie, dass dies ein gewisses "Integrations"-Testen ist, aber ich ziehe es stark vor, gegen eine "echte" Datenbank zu testen, da das Verspotten der Schnittstelle Ihnen nicht hilft, Probleme mit Ihren Abfragen/Abfragesyntax zu erkennen.

b) Die Alternative, obwohl auf der Anwendungsseite weniger erweiterbar, besteht darin, eine globale db *sql.DB zu haben Variable, die Sie in init() initialisieren innerhalb Ihrer Tests – da Tests keine garantierte Reihenfolge haben, müssen Sie init() verwenden – und führen Sie dann Ihre Tests von dort aus durch. d.h.

var db *sql.DB

func init() {
    var err error
    // Note the = and *not* the assignment - we don't want to shadow our global
    db, err = sqlx.Connect(...)
    if err != nil {
        ...
    }

    err := db.loadTestSchema
    // etc.
}

func TestGetUser(t *testing.T) {
   user := User{}
   exists, err := db.GetUser(user)
   ...
}

Einige praktische Beispiele finden Sie im GitHub-Repository von drone.io , und ich würde auch diesen Artikel über die Strukturierung von Go-Anwendungen (insbesondere das DB-Zeug).