Go のための Databricks SQL ドライバ

Databricks SQL Driver for Go は、Go コードを使用して Databricks コンピュート リソースで SQL コマンドを実行できるようにする Go ライブラリです。この記事では、Databricks SQL Driver for Go の READMEAPI リファレンスを補足します。

要件

Databricks SQL Driver for Go の概要

  1. Go 1.20 以降が既にインストールされ、既存の Go コード プロジェクトが既に作成されている開発用マシンで、次のように go mod init コマンドを実行して、Go コードの依存関係を追跡するための go.mod ファイルを作成します。

    go mod init sample
    
  2. go mod edit -require コマンドを実行して Databricks SQL Driver for Go パッケージに依存し、リリースに記載されている最新バージョンの Databricks SQL Driver for Go パッケージに置き換えv1.5.2

    go mod edit -require github.com/databricks/databricks-sql-go@v1.5.2
    

    go.modファイルは次のようになります。

    module sample
    
    go 1.20
    
    require github.com/databricks/databricks-sql-go v1.5.2
    
  3. プロジェクトで、Databricks SQL Driver for Go をインポートする Go コード ファイルを作成します。 次の例では、次の内容の main.go という名前のファイルに、Databricks ワークスペース内のすべてのクラスターが一覧表示されます。

    package main
    
    import (
      "database/sql"
      "os"
      _ "github.com/databricks/databricks-sql-go"
    )
    
    func main() {
      dsn := os.Getenv("DATABRICKS_DSN")
    
      if dsn == "" {
        panic("No connection string found. " +
         "Set the DATABRICKS_DSN environment variable, and try again.")
      }
    
      db, err := sql.Open("databricks", dsn)
      if err != nil {
        panic(err)
      }
      defer db.Close()
    
      if err := db.Ping(); err != nil {
        panic(err)
      }
    }
    
  4. 不足しているモジュールの依存関係を追加するには、 go mod tidy コマンドを実行します。

    go mod tidy
    

    go: warning: "all" matched no packagesエラーが発生した場合は、Databricks SQL Driver for Go をインポートする Go コード ファイルを追加するのを忘れていました。

  5. go mod vendor コマンドを実行して、main モジュール内のパッケージのビルドとテストをサポートするために必要なすべてのパッケージのコピーを作成します。

    go mod vendor
    
  6. 必要に応じてコードを変更し、Databricks 認証DATABRICKS_DSN 環境変数を設定します。「 DSN 接続文字列を使用して接続する」も参照してください。

  7. go run コマンドを実行して、main.goという名前のファイルを想定して、Go コード ファイルを実行します。

    go run main.go
    
  8. エラーが返されない場合は、Databricks ワークスペースで Databricks SQL Driver for Go が正常に認証され、そのワークスペースで実行中の Databricks クラスターまたは SQLウェアハウスに接続されています。

DSN 接続文字列を使用して接続する

クラスターと SQLウェアハウスにアクセスするには、 sql.Open() を使用して、データソース名 (DSN) 接続文字列を使用してデータベース ハンドルを作成します。 このコード例では、 DATABRICKS_DSNという名前の環境変数から DSN 接続文字列を取得します。

package main

import (
  "database/sql"
  "os"
  _ "github.com/databricks/databricks-sql-go"
)

func main() {
  dsn := os.Getenv("DATABRICKS_DSN")

  if dsn == "" {
    panic("No connection string found. " +
          "Set the DATABRICKS_DSN environment variable, and try again.")
  }

  db, err := sql.Open("databricks", dsn)
  if err != nil {
    panic(err)
  }
  defer db.Close()

  if err := db.Ping(); err != nil {
    panic(err)
  }
}

DSN 接続文字列を正しい形式で指定するには、「 認証」の DSN 接続文字列の例を参照してください。 たとえば、Databricks の個人用アクセストークン認証の場合は、次の構文を使用します。

  • <personal-access-token> 要件からのあなたのDatabricks個人用アクセストークンです。

  • <server-hostname> は、要件の サーバ ホスト名 の値です。

  • <port-number> は要件の ポート 値で、通常は 443です。

  • <http-path> は、要件の HTTP パス 値です。

  • <paramX=valueX> は、この記事で後述する 1 つ以上の 省略可能なパラメーター です。

token:<personal-access-token>@<server-hostname>:<port-number>/<http-path>?<param1=value1>&<param2=value2>

たとえば、クラスターの場合:

token:dapi12345678901234567890123456789012@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/protocolv1/o/1234567890123456/1234-567890-abcdefgh

たとえば、SQLウェアハウスの場合:

token:dapi12345678901234567890123456789012@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/endpoints/a1b234c5678901d2

セキュリティのベスト プラクティスとして、この DSN 接続文字列を Go コードにハードコーディングしないでください。 代わりに、この DSN 接続文字列を安全な場所から取得する必要があります。 たとえば、この記事の前半のコード例では、環境変数を使用しました。

省略可能なパラメーター

  • サポートされているオプションの接続パラメーターは、 <param=value>で指定できます。 より頻繁に使用されるものには、次のようなものがあります。

    • catalog: セッションの初期カタログ名を設定します。

    • schema: セッションの初期スキーマ名を設定します。

    • maxRows: 要求ごとにフェッチされる行の最大数を設定します。 デフォルトは 10000です。

    • timeout: サーバークエリー実行のタイムアウト (秒単位) を追加します。 デフォルトはタイムアウトなしです。

    • userAgentEntry: パートナーを識別するために使用されます。 詳細については、パートナーのドキュメントを参照してください。

  • サポートされているオプションのセッション・パラメーターは、 param=valueで指定できます。 より頻繁に使用されるものには、次のようなものがあります。

    • ansi_mode: Boolean 文字列。 セッション ステートメントが ANSI SQL 仕様で指定された規則に従うようにtrue します。システムのデフォルトは false です。

    • timezone: 文字列 ( America/Los_Angelesなど)。 セッションのタイムゾーンを設定します。 システムのデフォルトは UTC です。

たとえば、SQLウェアハウスの場合:

token:dapi12345678901234567890123456789012@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/endpoints/a1b234c5678901d2?catalog=hive_metastore&schema=example&maxRows=100&timeout=60&timezone=America/Sao_Paulo&ansi_mode=true

NewConnector機能で接続する

または、 sql.OpenDB() を使用して、 dbsql.NewConnector() で作成された新しいコネクタ オブジェクトを介してデータベース ハンドルを作成します (新しいコネクタ オブジェクトを使用して Databricks クラスターと SQLウェアハウスに接続するには、v1.0.0 以降の Databricks SQL Driver for Go が必要です)。 例えば:

package main

import (
  "database/sql"
  "os"
  dbsql "github.com/databricks/databricks-sql-go"
)

func main() {
  connector, err := dbsql.NewConnector(
    dbsql.WithAccessToken(os.Getenv("DATABRICKS_ACCESS_TOKEN")),
    dbsql.WithServerHostname(os.Getenv("DATABRICKS_HOST")),
    dbsql.WithPort(443),
    dbsql.WithHTTPPath(os.Getenv("DATABRICKS_HTTP_PATH")),
  )
  if err != nil {
    panic(err)
  }

  db := sql.OpenDB(connector)
  defer db.Close()

  if err := db.Ping(); err != nil {
    panic(err)
  }
}

正しい NewConnector 設定セットを指定するには、「 認証」の例を参照してください。

セキュリティのベストプラクティスとして、 NewConnector 設定をGoコードにハードコーディングしないでください。 代わりに、これらの値を安全な場所から取得する必要があります。 たとえば、上記のコードでは環境変数を使用しています。

より頻繁に使用される機能オプションには、次のようなものがあります。

  • WithAccessToken(<access-token>): 要件からの Databricks 個人用アクセストークン。 必須 string.

  • WithServerHostname(<server-hostname>): 要件の サーバーホスト名 の値。 必須 string.

  • WithPort(<port>): サーバーのポート番号 (通常は 443)。 必須 int.

  • WithHTTPPath(<http-path>): 要件の HTTP パス 値。 必須 string.

  • WithInitialNamespace(<catalog>, <schema>):セッション内のカタログ名とスキーマ名。 省略可能な string, string

  • WithMaxRows(<max-rows>): 要求ごとにフェッチされる最大行数。 デフォルトは [オプション 10000. intです。

  • WithSessionParams(<params-map>): "timezone" と "ansi_mode" を含むセッション パラメーター。 省略可能な map[string]string

  • WithTimeout(<timeout>).サーバークエリー実行のタイムアウト ( time.Duration単位)。 デフォルトはタイムアウトなしです。 随意。

  • WithUserAgentEntry(<isv-name-plus-product-name>).パートナーを識別するために使用されます。 詳細については、パートナーのドキュメントを参照してください。 省略可能な string

例:

connector, err := dbsql.NewConnector(
  dbsql.WithAccessToken(os.Getenv("DATABRICKS_ACCESS_TOKEN")),
  dbsql.WithServerHostname(os.Getenv("DATABRICKS_HOST")),
  dbsql.WithPort(443),
  dbsql.WithHTTPPath(os.Getenv("DATABRICKS_HTTP_PATH")),
  dbsql.WithInitialNamespace("samples", "nyctaxi"),
  dbsql.WithMaxRows(100),
  dbsql.SessionParams(map[string]string{"timezone": "America/Sao_Paulo", "ansi_mode": "true"}),
  dbsql.WithTimeout(time.Minute),
  dbsql.WithUserAgentEntry("example-user"),
)

認証

Databricks SQL Driver for Go では、次の Databricks 認証の種類がサポートされています。

Databricks個人用アクセストークン認証

Databricks SQL Driver for Go を Databricks 個人用アクセストークン認証で使用するには、まず Databricks 個人用アクセストークンを作成する必要があります。この手順の詳細については、「ワークスペース ユーザー向けの個人用アクセストークンDatabricks」を参照してください。

DSN 接続文字列と「DSN 接続文字列を使用して接続する」のコード例を使用して Databricks SQL Driver for Go を認証するには、次の DSN 接続文字列構文を使用します。

  • <personal-access-token> 要件からのあなたのDatabricks個人用アクセストークンです。

  • <server-hostname> は、要件の サーバ ホスト名 の値です。

  • <port-number> は要件の ポート 値で、通常は 443です。

  • <http-path> は、要件の HTTP パス 値です。

また、この記事で前述した 1 つ以上の 省略可能なパラメーター を追加することもできます。

token:<personal-access-token>@<server-hostname>:<port-number>/<http-path>

NewConnector 関数を使用して Databricks SQL Driver for Go を認証するには、次のコード スニペットと NewConnector 関数を使用した接続 のコード例を使用します (次の環境変数が設定されていることを前提としています)。

  • DATABRICKS_SERVER_HOSTNAMEをクラスターまたは SQLウェアハウスの [Server Hostname ] の値に設定します。

  • DATABRICKS_HTTP_PATHで、クラスターまたは SQLウェアハウスの HTTP パス 値に設定します。

  • DATABRICKS_TOKENを Databricks personal アクセストークンに設定します。

環境変数を設定するには、ご利用になっているオペレーティングシステムのドキュメントを参照してください。

connector, err := dbsql.NewConnector(
  dbsql.WithServerHostname(os.Getenv("DATABRICKS_SERVER_HOSTNAME")),
  dbsql.WithHTTPPath(os.Getenv("DATABRICKS_HTTP_PATH")),
  dbsql.WithPort(443),
  dbsql.WithAccessToken(os.Getenv("DATABRICKS_TOKEN")),
)

OAuthユーザー対マシン(U2M)認証

Databricks SQL Driver for Go バージョン 1.5.0 以降では、 OAuth ユーザーとマシン (U2M) 認証がサポートされています。

Databricks SQL Driver for Go を DSN 接続文字列と共に使用し、「DSN 接続文字列を使用して接続する」のコード例を使用するには、次の DSN 接続文字列構文を使用します。

  • <server-hostname> は、要件の サーバ ホスト名 の値です。

  • <port-number> は要件の ポート 値で、通常は 443です。

  • <http-path> は、要件の HTTP パス 値です。

また、この記事で前述した 1 つ以上の 省略可能なパラメーター を追加することもできます。

<server-hostname>:<port-number>/<http-path>?authType=OauthU2M

NewConnector 関数を使用して Databricks SQL Driver for Go を認証するには、まず import 宣言に以下を追加する必要があります。

"github.com/databricks/databricks-sql-go/auth/oauth/u2m"

次に、次のコード スニペットと「 NewConnector 関数を使用した接続」のコード例を使用して、次の環境変数が設定されていることを前提としています。

  • DATABRICKS_SERVER_HOSTNAMEをクラスターまたは SQLウェアハウスの [Server Hostname ] の値に設定します。

  • DATABRICKS_HTTP_PATHで、クラスターまたは SQLウェアハウスの HTTP パス 値に設定します。

環境変数を設定するには、ご利用になっているオペレーティングシステムのドキュメントを参照してください。

authenticator, err := u2m.NewAuthenticator(os.Getenv("DATABRICKS_SERVER_HOSTNAME"), 1*time.Minute)
if err != nil {
  panic(err)
}

connector, err := dbsql.NewConnector(
  dbsql.WithServerHostname(os.Getenv("DATABRICKS_SERVER_HOSTNAME")),
  dbsql.WithHTTPPath(os.Getenv("DATABRICKS_HTTP_PATH")),
  dbsql.WithPort(443),
  dbsql.WithAuthenticator(authenticator),
)

OAuthマシン間(M2M)認証

Databricks SQL Driver for Go バージョン 1.5.2 以降では、 OAuth マシン間 (M2M) 認証がサポートされています。

OAuth M2M 認証で Databricks SQL Driver for Go を使用するには、次の操作を行う必要があります。

  1. Databricks ワークスペースに Databricks サービスプリンシパルを作成し、そのサービスプリンシパルの OAuth シークレットを作成します。

    サービスプリンシパルとそのOAuth シークレットを作成するには、「 Databricksを使用してサービスプリンシパルで へのアクセスを認証するOAuth (OAuth M2M)」 を参照してください。サービスプリンシパルの UUID または Application ID の値と、サービスプリンシパルの シークレットの SecretOAuth 値をメモします。

  2. そのサービスプリンシパルにクラスターまたはウェアハウスへのアクセス権を付与します。

    サービスプリンシパルにクラスターまたはウェアハウスへのアクセスを許可するには、 「コンピュート権限」または「SQL ウェアハウスの管理」を参照してください。

DSN 接続文字列と「DSN 接続文字列を使用して接続する」のコード例を使用して Databricks SQL Driver for Go を認証するには、次の DSN 接続文字列構文を使用します。

  • <server-hostname> は、要件の サーバ ホスト名 の値です。

  • <port-number> は要件の ポート 値で、通常は 443です。

  • <http-path> は、要件の HTTP パス 値です。

  • <client-id> は、サービスプリンシパルの UUID または アプリケーション ID の値です。

  • <client-secret> は、サービスプリンシパルの OAuth シークレットの Secret 値です。

また、この記事で前述した 1 つ以上の 省略可能なパラメーター を追加することもできます。

<server-hostname>:<port-number>/<http-path>?authType=OAuthM2M&clientID=<client-id>&clientSecret=<client-secret>

NewConnector 関数を使用して Databricks SQL Driver for Go を認証するには、まず import 宣言に以下を追加する必要があります。

"github.com/databricks/databricks-sql-go/auth/oauth/m2m"

次に、次のコード スニペットと「 NewConnector 関数を使用した接続」のコード例を使用して、次の環境変数が設定されていることを前提としています。

  • DATABRICKS_SERVER_HOSTNAMEをクラスターまたは SQLウェアハウスの [Server Hostname ] の値に設定します。

  • DATABRICKS_HTTP_PATHで、クラスターまたは SQLウェアハウスの HTTP パス 値に設定します。

  • DATABRICKS_CLIENT_IDは、サービスプリンシパルの UUID または アプリケーション ID の値に設定されます。

  • DATABRICKS_CLIENT_SECRETで、サービスプリンシパルの OAuth シークレットの Secret 値に設定します。

環境変数を設定するには、ご利用になっているオペレーティングシステムのドキュメントを参照してください。

authenticator := m2m.NewAuthenticator(
  os.Getenv("DATABRICKS_CLIENT_ID"),
  os.Getenv("DATABRICKS_CLIENT_SECRET"),
  os.Getenv("DATABRICKS_SERVER_HOSTNAME"),
)

connector, err := dbsql.NewConnector(
  dbsql.WithServerHostname(os.Getenv("DATABRICKS_SERVER_HOSTNAME")),
  dbsql.WithHTTPPath(os.Getenv("DATABRICKS_HTTP_PATH")),
  dbsql.WithPort(443),
  dbsql.WithAuthenticator(authenticator),
)

データのクエリー

次のコード例は、Databricks コンピュート リソースに対して基本的な SQL クエリーを実行するために Databricks SQL Driver for Go を呼び出す方法を示しています。 このコマンドは、samples カタログのnyctaxiスキーマのtripsテーブルから最初の 2 行を返します。

このコード例では、DATABRICKS_DSNという名前の環境変数から DSN 接続文字列を取得します。

package main

import (
  "database/sql"
  "fmt"
  "os"
  "time"

  _ "github.com/databricks/databricks-sql-go"
)

func main() {
  dsn := os.Getenv("DATABRICKS_DSN")

  if dsn == "" {
    panic("No connection string found." +
          "Set the DATABRICKS_DSN environment variable, and try again.")
  }

  db, err := sql.Open("databricks", dsn)
  if err != nil {
    panic(err)
  }

  defer db.Close()

  var (
    tpep_pickup_datetime  time.Time
    tpep_dropoff_datetime time.Time
    trip_distance         float64
    fare_amount           float64
    pickup_zip            int
    dropoff_zip           int
  )

  rows, err := db.Query("SELECT * FROM samples.nyctaxi.trips LIMIT 2")
  if err != nil {
    panic(err)
  }

  defer rows.Close()

  fmt.Print("tpep_pickup_datetime,",
    "tpep_dropoff_datetime,",
    "trip_distance,",
    "fare_amount,",
    "pickup_zip,",
    "dropoff_zip\n")

  for rows.Next() {
    err := rows.Scan(&tpep_pickup_datetime,
      &tpep_dropoff_datetime,
      &trip_distance,
      &fare_amount,
      &pickup_zip,
      &dropoff_zip)
    if err != nil {
      panic(err)
    }

    fmt.Print(tpep_pickup_datetime, ",",
      tpep_dropoff_datetime, ",",
      trip_distance, ",",
      fare_amount, ",",
      pickup_zip, ",",
      dropoff_zip, "\n")
  }

  err = rows.Err()
  if err != nil {
    panic(err)
  }
}

Unity Catalogボリューム内のファイルを管理する

Databricks SQLドライバーを使用すると、次の例に示すように、 Unity Catalogボリュームにローカル ファイルを書き込んだり、ボリュームからファイルをダウンロードしたり、ボリュームからファイルを削除したりできます。

package main

import (
  "context"
  "database/sql"
  "os"

  _ "github.com/databricks/databricks-sql-go"
  "github.com/databricks/databricks-sql-go/driverctx"
)

func main() {
  dsn := os.Getenv("DATABRICKS_DSN")

  if dsn == "" {
    panic("No connection string found." +
      "Set the DATABRICKS_DSN environment variable, and try again.")
  }

  db, err := sql.Open("databricks", dsn)
  if err != nil {
    panic(err)
  }
  defer db.Close()

  // For writing local files to volumes and downloading files from volumes,
  // you must first specify the path to the local folder that contains the
  // files to be written or downloaded.
  // For multiple folders, add their paths to the following string array.
  // For deleting files in volumes, this string array is ignored but must
  // still be provided, so in that case its value can be set for example
  // to an empty string.
  ctx := driverctx.NewContextWithStagingInfo(
    context.Background(),
    []string{"/tmp/"},
  )

  // Write a local file to the path in the specified volume.
  // Specify OVERWRITE to overwrite any existing file in that path.
  db.ExecContext(ctx, "PUT '/tmp/my-data.csv' INTO '/Volumes/main/default/my-volume/my-data.csv' OVERWRITE")

  // Download a file from the path in the specified volume.
  db.ExecContext(ctx, "GET '/Volumes/main/default/my-volume/my-data.csv' TO '/tmp/my-downloaded-data.csv'")

  // Delete a file from the path in the specified volume.
  db.ExecContext(ctx, "REMOVE '/Volumes/main/default/my-volume/my-data.csv'")

  db.Close()
}

伐採

Databricks SQL Driver for Go が発行するメッセージをログに記録するには、 github.com/databricks/databricks-sql-go/loggerを使用します。 次のコード例では、 sql.Open()を使用して、DSN 接続文字列を通じてデータベース ハンドルを作成します。 このコード例は、 DATABRICKS_DSNという名前の環境変数から DSN 接続文字列を取得します。 debugレベル以下で出力されるすべてのログメッセージは、results.logファイルに書き込まれます。

package main

import (
  "database/sql"
  "io"
  "log"
  "os"

  _ "github.com/databricks/databricks-sql-go"
  dbsqllog "github.com/databricks/databricks-sql-go/logger"
)

func main() {
  dsn := os.Getenv("DATABRICKS_DSN")

  // Use the specified file for logging messages to.
  file, err := os.Create("results.log")
  if err != nil {
    log.Fatal(err)
  }
  defer file.Close()

  writer := io.Writer(file)

  // Log messages at the debug level and below.
  if err := dbsqllog.SetLogLevel("debug"); err != nil {
    log.Fatal(err)
  }

  // Log messages to the file.
  dbsqllog.SetLogOutput(writer)

  if dsn == "" {
    panic("Error: Cannot connect. No connection string found. " +
      "Set the DATABRICKS_DSN environment variable, and try again.")
  }

  db, err := sql.Open("databricks", dsn)
  if err != nil {
    panic(err)
  }
  defer db.Close()

  if err := db.Ping(); err != nil {
    panic(err)
  }
}

テスティング

コードをテストするには、テスト標準ライブラリなどの Go テスト フレームワークを使用します。 Databricks REST API エンドポイントを呼び出さずに、または Databricks アカウントやワークスペースの状態を変更せずに、シミュレートされた条件下でコードをテストするには、 testfifyなどの Go モック ライブラリを使用します。

たとえば、Databricks ワークスペース接続を返すGetDBWithDSNPAT関数、 samplesカタログのnyctaxiスキーマのtripsテーブルからデータを返すGetNYCTaxiTrips関数、および返されたデータを出力するPrintNYCTaxiTripsを含むhelpers.goという名前の次のファイルがあるとします。

package main

import (
  "database/sql"
  "fmt"
  "strconv"
  "time"
)

func GetDBWithDSNPAT(dsn string) (*sql.DB, error) {
  db, err := sql.Open("databricks", dsn)
  if err != nil {
    return nil, err
  }
  return db, nil
}

func GetNYCTaxiTrips(db *sql.DB, numRows int) (*sql.Rows, error) {
  rows, err := db.Query("SELECT * FROM samples.nyctaxi.trips LIMIT " + strconv.Itoa(numRows))
  if err != nil {
    return nil, err
  }
  return rows, nil
}

func PrintNYCTaxiTrips(rows *sql.Rows) {
  var (
    tpep_pickup_datetime  time.Time
    tpep_dropoff_datetime time.Time
    trip_distance         float64
    fare_amount           float64
    pickup_zip            int
    dropoff_zip           int
  )

  fmt.Print(
    "tpep_pickup_datetime,",
    "tpep_dropoff_datetime,",
    "trip_distance,",
    "fare_amount,",
    "pickup_zip,",
    "dropoff_zip\n",
  )

  for rows.Next() {
    err := rows.Scan(
      &tpep_pickup_datetime,
      &tpep_dropoff_datetime,
      &trip_distance,
      &fare_amount,
      &pickup_zip,
      &dropoff_zip,
    )
    if err != nil {
      panic(err)
    }

    fmt.Print(
      tpep_pickup_datetime, ",",
      tpep_dropoff_datetime, ",",
      trip_distance, ",",
      fare_amount, ",",
      pickup_zip, ",",
      dropoff_zip, "\n",
    )
  }

  err := rows.Err()
  if err != nil {
    panic(err)
  }
}

そして、これらの関数を呼び出す main.go という名前の次のファイルがあるとします。

package main

import (
  "os"
)

func main() {
  db, err := GetDBWithDSNPAT(os.Getenv("DATABRICKS_DSN"))
  if err != nil {
    panic(err)
  }

  rows, err := GetNYCTaxiTrips(db, 2)
  if err != nil {
    panic(err)
  }

  PrintNYCTaxiTrips(rows)
}

次の helpers_test.go という名前のファイルは、 GetNYCTaxiTrips 関数が予期される応答を返すかどうかをテストします。 このテストは、ターゲットワークスペースへの実際の接続を作成するのではなく、 sql.DBオブジェクトをモックします。 また、このテストでは、実際のデータに含まれるスキーマと値に準拠する一部のデータをモックします。 このテストでは、モックされた接続を介してモックされたデータが返され、モックされたデータ行の 1 つの値が期待値と一致するかどうかがチェックされます。

package main

import (
  "database/sql"
  "testing"

  "github.com/stretchr/testify/assert"
  "github.com/stretchr/testify/mock"
)

// Define an interface that contains a method with the same signature
// as the real GetNYCTaxiTrips function that you want to test.
type MockGetNYCTaxiTrips interface {
  GetNYCTaxiTrips(db *sql.DB, numRows int) (*sql.Rows, error)
}

// Define a struct that represents the receiver of the interface's method
// that you want to test.
type MockGetNYCTaxiTripsObj struct {
  mock.Mock
}

// Define the behavior of the interface's method that you want to test.
func (m *MockGetNYCTaxiTripsObj) GetNYCTaxiTrips(db *sql.DB, numRows int) (*sql.Rows, error) {
  args := m.Called(db, numRows)
  return args.Get(0).(*sql.Rows), args.Error(1)
}

func TestGetNYCTaxiTrips(t *testing.T) {
  // Instantiate the receiver.
  mockGetNYCTaxiTripsObj := new(MockGetNYCTaxiTripsObj)

  // Define how the mock function should be called and what it should return.
  // We're not concerned with whether the actual database is connected to--just
  // what is returned.
  mockGetNYCTaxiTripsObj.On("GetNYCTaxiTrips", mock.Anything, mock.AnythingOfType("int")).Return(&sql.Rows{}, nil)

  // Call the mock function that you want to test.
  rows, err := mockGetNYCTaxiTripsObj.GetNYCTaxiTrips(nil, 2)

  // Assert that the mock function was called as expected.
  mockGetNYCTaxiTripsObj.AssertExpectations(t)

  // Assert that the mock function returned what you expected.
  assert.NotNil(t, rows)
  assert.Nil(t, err)
}

GetNYCTaxiTrips 関数には SELECT ステートメントが含まれているため、trips テーブルの状態は変更されないため、この例ではモック作成は絶対に必要というわけではありません。ただし、モックを使用すると、ワークスペースとの実際の接続が確立されるのを待たずに、テストをすばやく実行できます。 また、モックを使用すると、 INSERT INTOUPDATEDELETE FROMなど、テーブルの状態を変更する可能性のある関数に対して、シミュレートされたテストを複数回実行できます。

関連リソース