Mysql
 sql >> Base de données >  >> RDS >> Mysql

Développement piloté par les tests pour vérifier les requêtes de base de données impliquées dans les méthodes

J'ai eu une question similaire il n'y a pas longtemps lors de la refactorisation de certains de mes propres tests, et il y a plusieurs façons de le faire :

a) Indiquez un type exporté et un Open ou Connect fonction qui le renvoie - par exemple

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
}

... puis chacun de vos teste, écrit des fonctions de configuration et de démontage qui renvoient une instance de *DB sur lesquelles vous définissez vos fonctions de base de données (en tant que méthodes - c'est-à-dire 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
}

Notez qu'il s'agit en quelque sorte d'un test "d'intégration", mais je préfère fortement tester par rapport à une "vraie" base de données car se moquer de l'interface ne vous aidera pas à détecter les problèmes avec vos requêtes/syntaxe de requête.

b) L'alternative, bien que moins extensible côté application, est d'avoir une db *sql.DB globale variable que vous initialisez dans init() dans vos tests - puisque les tests n'ont pas d'ordre garanti, vous devrez utiliser init() – puis exécutez vos tests à partir de là. c'est-à-dire

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)
   ...
}

Vous pouvez trouver des exemples pratiques dans le référentiel GitHub de drone.io , et je recommanderais également cet article sur la structuration des applications Go (en particulier les trucs DB).