henriのメモ

henriのメモ

日曜画家のようにとは行かないけれど、学んだことや考えたことを残します

Go言語の学習メモと情報源まとめ

『プロになるためのWeb技術入門』の新版はGo言語で解説になったんだなあと思いつつ読み進めていた。 普段はJavaばかり触っているが、だいぶ気色の違う言語のようで興味が湧いてきた。 例えば、書籍中によると以下のような特徴があるとのことだった。

これだけでもJavaと大きな違いがあることがわかる。
普段はJava + Springでバックエンドの開発・保守をやっているが、Goだとどうなるのか調べながら触ってみようと思う。

前提

私の環境は以下の通り

  • M2MacBook Pro
  • メモリは24GB
  • MacOS Sonoma 14.6.1

環境構築

Goの公式よりpkgファイルの取得もできたが、brewコマンドでインストールした。

$ brew install go 

VSCodeを使っているので、以下の拡張機能を追加した。

Go - Visual Studio Marketplace

手を動かしてみる

公式チュートリアルがたくさん用意されている。 以下、私の感想ベースでメモを残しておく。(単に手順とか記載してもあまり意味がないので)

Tutorials - The Go Programming Language

まずはGetting Started

go mod tidyについて

このコマンドを実行すると不足しているrequirements(依存ライブラリ?)を追加してくれたり、また不要なrequirementsを削除してくれる。

  • 初期状態

hello.go(mainの関数を持つコード)

package main

import "fmt"

func main() {
    fmt.Println("Hello World!")
}

go.mod

module example/hello

go 1.23.4
  • hello.goに"rsc.io/quote/v4"を追加してgo mod tidyを実行

hello.go(mainの関数を持つコード)

package main

import (
    "fmt"

    "rsc.io/quote/v4"
)

func main() {
    fmt.Println(quote.Go())
}

go.mod

module example/hello

go 1.23.4

require rsc.io/quote/v4 v4.0.1

require (
    golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
    rsc.io/sampler v1.3.0 // indirect
)
  • hello.goから"rsc.io/quote/v4"を削除してgo mod tidyを実行

hello.go(mainの関数を持つコード)

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World!")
}

go.mod

module example/hello

go 1.23.4

追加したり削除したりをよしなにやってくれている。便利。

hello.goに"rsc.io/quote/v4"を追加してgo mod tidyを実行したときに requireが2箇所追加されたが、上のrequireはプロジェクトが直接依存しているモジュールで、 したのrequireは間接的に依存しているモジュール。

Gradleでも間接的に依存しているライブラリを持ってきてくれるので同じようなものかな。

Go Modules Reference - The Go Programming Language

Tutorial: Get started with Go - The Go Programming Language

文法に慣れる A Tour of GO

tour of go をローカルで動かしたいのに動かない

$ go tool tour
go: no such tool "tour"
$ go get github.com/atotto/go-tour-jp/gotour
go: go.mod file not found in current directory or any parent directory.
        'go get' is no longer supported outside a module.
        To build and install a command, use 'go install' with a version,
        like 'go install example.com/cmd@latest'
        For more information, see https://golang.org/doc/go-get-install-deprecation
        or run 'go help get' or 'go help install'.

原因調査が必要。同じところでハマってる人を発見した。

【Go言語】A Tour Of Go (日本語版) のローカル実行エラー - lelelemon’s blog

この方が残してくれているように、英語版の方にはコマンドが記載されている。

A Tour of Go

$ go install golang.org/x/website/tour@latest 

コマンドを実行したところ、~/go/bin にtourが追加されていることを確認できた。
そして~/go/bin/tourを実行して無事にローカル起動ができた。
英語版になるけど、日本語版はメンテの維持が難しい(最終コミットが2020年12月)ようだったので英語版のまま進める。

簡潔な解説と実際のコード例で見比べられるので、英語でもほぼ困らない感じだった。

A Tour of Go

Goの観点でプログラミングを眺めるCreate a module

手を動かして確認していないが、以下の情報源がある。

Tutorial: Create a Go module - The Go Programming Language

Goでマルチモジュールワークスペースを作成して使用する

手を動かして確認していないが、以下の情報源がある。

Tutorial: Getting started with multi-module workspaces - The Go Programming Language

RDBへのアクセス

手を動かして確認していないが、以下の情報源がある。

Tutorial: Accessing a relational database - The Go Programming Language

RESTful APIをGinフレームワークを使って実装する

未着手。。。

Tutorial: Developing a RESTful API with Go and Gin - The Go Programming Language

参考(全体通して)

The Go Programming Language

Go Packages - Go Packages

[改訂新版]プロになるためのWeb技術入門:書籍案内|技術評論社

srs/test/java配下にテストクラス、メソッドを配置してユニットテストが実行できるのはなぜか

概要

今まで当たり前のようにJUnitを使用してユニットテストを実行してきたが、
それがどんな仕組みで動いているのか考えたことがなかったので調べてみた。

環境

openjdk 21.0.4 2024-07-16 LTS
OpenJDK Runtime Environment Corretto-21.0.4.7.1 (build 21.0.4+7-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.4.7.1 (build 21.0.4+7-LTS, mixed mode, sharing)

問題の所在

例えば、以下のようなサンプルプロジェクトがある。

src/main以下にはCalculator.javaというクラスのみ持ち、src/test配下にはそのテストクラスのみ持っているシンプルなプロジェクトとなっている。

Calculator.javaとそのテストクラスCalculatorTest.javaは以下の通り。

この時点でテストを実行する場合は成功する。

気になるのはここからである。
試しにsrc/testをリネームしてsrc/testUTとすると、以下のようにエラーを吐く。

Internal Error occurred.
org.junit.platform.commons.JUnitException: TestEngine with ID 'junit-jupiter' failed to discover tests
    at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:160)
    at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:132)
    at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:78)
    at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:99)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:85)
    at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:63)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.junit.platform.commons.JUnitException: ClassSelector [className = 'org.example.CalculatorTest', classLoader = null] resolution failed
    at org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener.selectorProcessed(AbortOnFailureLauncherDiscoveryListener.java:39)
    at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:103)
    at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:83)
    at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:113)
    at org.junit.jupiter.engine.discovery.DiscoverySelectorResolver.resolveSelectors(DiscoverySelectorResolver.java:46)
    at org.junit.jupiter.engine.JupiterTestEngine.discover(JupiterTestEngine.java:69)
    at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:152)
    ... 13 more
Caused by: org.junit.platform.commons.PreconditionViolationException: Could not load class with name: org.example.CalculatorTest
    at org.junit.platform.engine.discovery.ClassSelector.lambda$getJavaClass$0(ClassSelector.java:95)
    at org.junit.platform.commons.function.Try$Failure.getOrThrow(Try.java:335)
    at org.junit.platform.engine.discovery.ClassSelector.getJavaClass(ClassSelector.java:94)
    at org.junit.jupiter.engine.discovery.ClassSelectorResolver.resolve(ClassSelectorResolver.java:66)
    at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.lambda$resolve$2(EngineDiscoveryRequestResolution.java:135)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1685)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
    at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:189)
    at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:126)
    at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:92)
    ... 18 more
Caused by: java.lang.ClassNotFoundException: org.example.CalculatorTest
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:534)
    at java.base/java.lang.Class.forName(Class.java:513)
    at org.junit.platform.commons.util.ReflectionUtils.lambda$tryToLoadClass$9(ReflectionUtils.java:832)
    at org.junit.platform.commons.function.Try.lambda$call$0(Try.java:57)
    at org.junit.platform.commons.function.Try.of(Try.java:93)
    at org.junit.platform.commons.function.Try.call(Try.java:57)
    at org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(ReflectionUtils.java:795)
    at org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(ReflectionUtils.java:751)
    at org.junit.platform.engine.discovery.ClassSelector.getJavaClass(ClassSelector.java:92)
    ... 32 more

Process finished with exit code -2

エラーメッセージを要約すると次のようになる。

  • ID 'junit-jupiter' を持つ TestEngine がテストの検出に失敗した。
  • クラスセレクタ [className = 'org.example.CalculatorTest', classLoader = null] の解決に失敗した。
  • org.example.CalculatorTestの名前を持つクラスをロードできなかった。
  • org.example.CalculatorTestというクラスが見つからなかった。

なぜこのようなことが起きるのだろうか。
次節ではこれらの原因について調査していく。

調べたこと

手始めに、geminiに対してこんな質問を投げてみた。

javaのプロジェクトでsrc/testというフォルダがある。
このtestフォルダの名前をtestUTにしたところ、以下のエラーを吐いた。
その理由はなぜか。
Internal Error occurred.
org.junit.platform.commons.JUnitException: TestEngine with ID 'junit-jupiter' failed to discover tests

このような回答を得た(一部抜粋、改変)。

原因について
 ビルドパス設定の誤り:
  テストソースルートの指定: IDEやビルドツール(Maven、Gradleなど)の設定で、テストソースルートとして src/testUT フォルダが正しく指定されているか確認してください。
  依存ライブラリのパス: JUnitなどのテストフレームワークのライブラリが、テストクラスから正しく参照できるパスに設定されているか確認してください。
解決策
 ビルドパス設定の確認・修正:
 IDE:
  Eclipse: プロジェクトのプロパティ → Javaのビルド・パス → ソース → フォルダーの追加
  IntelliJ IDEA: モジュール設定 → ソース
 Maven: pom.xmlファイルの<sourceDirectory>と<testSourceDirectory>要素を確認・修正
 Gradle: build.gradleファイルのsourceSetsブロックを確認・修正

回答を読む限り、テストソースルートを正しく設定すれば解決できそう。
次節では下記を参考に再設定を行うこととする。
テストの準備 | IntelliJ IDEA ドキュメント

調査結果をもとに再設定

build.gradleを開き、次のコードを追加する。
src/testUTをテストルートとして使用するように設定。

sourceSets {
    test {
        java {
            srcDirs = ['src/testUT']
        }
    }
}

その後もう一度テストを実行してみたところ成功した。

まとめ

テストルートの設定を自ら行うことで少し理解が深まった気がする。

GitHub CLI 個人的コマンドメモ

概要

何回も同じことを調べてしまっているので、使ったコマンドはここにメモしておく。

コマンド

既存のローカルリポジトリGitHubへプッシュする方法

$ gh repo create

「What would you like todo?」と聞かれるので、「Push an existing local repository to GitHub」を選択する。

求人票は学習の参考になる

何を勉強したらいいのかわからない場合のTips

 

自分ではちょっと難しいだろうな、と思っている会社の求人票を見てみるといいかもしれない。

 

そこで求められている人物像・技術が業として開発する場合の参考になる。

 

副次的な効果として、世間で使われている技術と現職の技術の比較もできる。

 

Dockerのチュートリアルでハマったポイント/気になったポイント

概要

Dockerのチュートリアルでハマったポイントをまとめる。

環境準備

準備としては、下記サイトに従ってローカル環境でチュートリアル専用ページを起動 www.docker.com

実行環境

  • M2 MacBook pro
  • Docker version 26.1.4, build 5650f9b
  • Docker Compose version v2.27.1-desktop.1

ハマったポイント/気になったポイント

Play with Dockerでアプリが起動しなかった

チュートリアルに従ってgetting-sratedイメージを作成してDockerHubにプッシュした後、
Play with Dockerにて新しいインスタンスを作成してアプリを起動しようとした。 しかし起動できなかった。

$ docker run -dp 3000:3000 USER-NAME/getting-started
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64/v3) and no specific platform was requested

プロセスの起動状況を確認したが、やはり起動していない。

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

警告メッセージを読む限り、
要求されたイメージのプラットフォーム(linux/arm64/v8)が、検出されたホストのプラットフォーム(linux/amd64/v3)と一致していないとのこと。

調べたところ、docker build時にプラットフォームのオプションを指定することで解決できそうだった。

プラットフォームにlinux/amd64を指定。

docker build --platform linux/amd64 -t getting-started:linux-amd64 .

プッシュする。(latestと分けておきたかったのでtagを別で作成して指定)

docker push USER-NAME/getting-started:linux-amd64

Play with Dockerのターミナル上で、

$ docker run -dp 3000:3000 USER-NAME/getting-started:linux-amd64

先ほどの警告メッセージは出なくなり、プロセスも起動した。

$ docker ps
CONTAINER ID   IMAGE                                    COMMAND                  CREATED              STATUS              PORTS                    NAMES
7f8a9e6c7329   USER-NAME/getting-started:linux-amd64   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:3000->3000/tcp   gallant_mclaren


参考

mysqlのDBにテーブルが作成済みだったことについて

こちらのハンズオンについての疑問点。

http://localhost/tutorial/multi-container-apps/

下記のコマンドで作成した際、すでにテーブルが作成されていた。
裏で何が起きていたのか気になる。

docker run -d \
    --network todo-app --network-alias mysql \
    -v todo-mysql-data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=secret \
    -e MYSQL_DATABASE=todos \
    mysql:8.0

JavaScriptで初期化のコードがあった。
/app/index.jsにてDBの初期化関数 init()を呼び出しており、
init()の内部でCREATE文を実行してテーブルを作成している。

つまり、上記のコマンド実行時にテーブルが作成されたわけではなく、
下記のコマンド実行時に作成されたものと判断した。
本件は私の勘違いだった。

docker run -dp 3000:3000 \
  -w /app -v "$(pwd):/app" \
  --network todo-app \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD=secret \
  -e MYSQL_DB=todos \
  node:18-alpine \
  sh -c "yarn install && yarn run dev"

docker scanコマンドが見つからない

チュートリアルに従って進めていたところ、下記コマンドで詰まった。

$ docker scan getting-started
docker: 'scan' is not a docker command.
See 'docker --help'

参考にした下記サイトによると、docker scanはすでに削除されている。
docker scoutコマンドを使うとのこと。
※コマンドの詳しい使用法は公式ドキュメントを参照すること

うまくいったようだ

$ docker scout cves getting-started
    ✓ Image stored for indexing
    ✓ Indexed 613 packages
    ✗ Detected 15 vulnerable packages with a total of 19 vulnerabilities

(以下省略)

参考

docker run -d の -dをつけない場合どうなるか

まず、-dオプションについて
→デタッチモードでコンテナが起動される。バックグラウンドで実行されるため、標準出力がコンソール上に表示されない。

$ docker run -d getting-started
e4ad9926facf8cd18a33de8466c20395848e70092dde5b2df2ad12d4e59d1199
$

-dオプションをつけないとどうなるのか
→フォアグラウンドで実行される

$ docker run getting-started
Using sqlite database at /etc/todos/todo.db
Listening on port 3000

特に理由がなければ常に-dオプションをつけるのが良さそう。

参考

労働関連の情報源

概要

労働環境を取り巻くルールは常に更新されており、
自分の身を守るためにそれらの知識は最低限把握しておく必要があると考えている。
そこで、情報源となるサイトをまとめておくこととした。

情報源

anzeninfo.mhlw.go.jp

www.mhlw.go.jp

www.mhlw.go.jp

sr-search.com

roumu.com

jinjibu.jp

www.rodo.co.jp