0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go言語の単体テストにおける[][]stringデータをマルチパートファイルに変換する実装例

Last updated at Posted at 2025-03-04

Go言語でデータをCSVファイルとしてマルチパート形式に変換する方法

概要

Go言語でCSVデータの情報を元にDBに更新をかけるAPIを実装をしており、その単体テストの中でmultipart.File 型のテストデータを準備するのにすごく苦労したので記事にしました。
この関数は、[][]string 型のデータを受け取ってマルチパートのCSVファイルに変換し、単体テストなどで利用可能です。

関数の実装

以下の関数 createMultipartDataFromSlice は、[][]string 型のデータを受け取り、それをマルチパート形式のCSVファイルに変換して返します。

package interactor_test

// import は省略

func TestInteractor_Invoke(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockHogeRepo := mocks.NewMockHogeRepository(ctrl)

	interactor := interactor.NewInteractor(
            mockHogeRepo,
        )

	data := [][]string{
        {"header1", "header2", "header3"},
        {"row1_col1", "row1_col2", "row1_col3"},
        {"row2_col1", "row2_col2", "row2_col3"},
    }
    
    // 今回紹介するmultipart.Fileを生成する関数
    file, err := createMultipartDataFromSlice(data)
    if err != nil {
        fmt.Printf("Failed to create multipart file: %v\n", err)
        return
    }
    defer file.Close()

    content, err := io.ReadAll(file)
    if err != nil {
        fmt.Printf("Failed to read CSV: %v\n", err)
        return
    }
    fmt.Println(string(content))

    ctx := context.Background()
    testCases := []struct {
        // Field
        setupMocks    func()
        expectedError error
    }{
        {
            // test cases
        },
    }
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			tc.setupMocks()
			err := interactor.Invoke(ctx, file)
			if tc.expectedError != nil {
				assert.Error(t, err)
				assert.Equal(t, tc.expectedError, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

// 指定された`[][]string`型のデータをマルチパートデータに変換
func createMultipartDataFromSlice(data [][]string) (multipart.File, error) {
	var buf bytes.Buffer
	writer := multipart.NewWriter(&buf)

	// フォームファイルとしてデータを追加する
	part, err := writer.CreateFormFile("file", "test.csv")
	if err != nil {
		return nil, fmt.Errorf("error creating form file: %w", err)
	}

	// CSVライターを作成してデータを書き込む
	csvWriter := csv.NewWriter(part)
	err = csvWriter.WriteAll(data)
	if err != nil {
		return nil, fmt.Errorf("error writing data to CSV: %w", err)
	}

	// ライターを閉じてマルチパートデータを完成させる
	csvWriter.Flush()
	writer.Close()

	// バッファを *os.File に変換する一時ファイルを作成する
	tmpFile, err := os.CreateTemp("", "test-*.csv")
	if err != nil {
		return nil, fmt.Errorf("error creating temp file: %w", err)
	}
	_, err = tmpFile.Write(buf.Bytes())
	if err != nil {
		return nil, fmt.Errorf("error writing to temp file: %w", err)
	}

	// tmpFileを再度読み取れるようにカーソルを先頭に戻す
	if _, err := tmpFile.Seek(0, io.SeekStart); err != nil {
		return nil, fmt.Errorf("error seeking temp file: %w", err)
	}

	return tmpFile, nil
}

=== RUN   testInteractor_Invoke
--56d604397513fcbd3154df6a39aa9da7bfe68612e54db46788fd4faf5ceb
Content-Disposition: form-data; name="file"; filename="test.csv"
Content-Type: application/octet-stream

header1,header2,header3
row1_col1,row1_col2,row1_col3
row2_col1,row2_col2,row2_col3

まとめ
この関数を使用することで、Goプログラム内でテストしたいcsvデータを[][]stringで準備してあげつ事でマルチパート形式のファイルに変換し、単体テストや他の用途(実装でも使えたりする?)で利用できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?