背景
初めてGo言語を使ってAPIを作っており、テストを書くことに。
以前、Javaを使った開発をしていたので、そのノリでテストを書こうとしたのですが...
書いてみると、mockが上手く使えない...
そこで、Googleさんに色々聞いていたら、どうやらDIというものがあるらしいってなった次第です。
なお、ここに書いてることは自分なりの解釈なので、間違い等ありましたらコメントにてご指摘をお願いします!
DIとは
参考:
猿でも分かる! Dependency Injection: 依存性の注入
やはりあなた方のDependency Injectionはまちがっている。
Dependency Injection=依存性の注入
どうやらDIとは、Dependency Injectionの略のようで、日本語では「依存性の注入」と訳されるらしい。
しかし、依存性の注入とはよく分からない単語だ。
「依存性」と「注入」
以下、自分なりの「依存性」と「注入」の理解になります。
- 依存性
依存性とは、あるインスタンス内で他のインスタンスを利用している状態のこと。 - 注入
インスタンス内で利用する部品をインスタンス化する段階で渡してあげること。
つまり
う〜ん?要は、インスタンス化するときにそのインスタンス内で使うものを渡してあげれば良い?
Example
今回は簡単に、フレームワークのginを利用してHelloWorldページを実装してみます。
package action
// ServiceImp is an implimentation of service
type ServiceImp struct {
}
// HelloService return a hello message
func (s ServiceImp) HelloService() string {
return "Hello World!!"
}
こちらが、サービスのロジックを実装したStructになります。
今回は、Hello World!!と返すだけです。
package action
import "github.com/gin-gonic/gin"
// ServiceIF is the interface
type ServiceIF interface {
HelloService() string
}
// Env is service environment
type Env struct {
Service ServiceIF
}
// NewEnv Constractor for Env
func NewEnv(service ServiceIF) *Env {
env := Env{Service: service}
return &env
}
// HelloWorld is the function to display a message
func (e *Env) HelloWorld(c *gin.Context) {
c.JSON(200, gin.H{"msg": e.Service.HelloService()})
}
そして、他に、ServiceImp
を受け取れるインターフェースを持ったEnv
を定義します。
package main
import (
"sample/action"
"github.com/gin-gonic/gin"
)
func main() {
var service action.ServiceImp
env := action.NewEnv(service)
r := gin.Default()
r.GET("/", env.HelloWorld)
r.Run()
}
上記2つのstructをmain関数の中でインスタンス化します。
このときに、Env
のインスタンスであるenv
にはServiceImp
のインスタンスであるservice
を渡します。
これで、http://localhost:8080
にアクセスすると、無事に”Hello World!!”が表示されました。
利点
こうすることで何が良いかというと。。。
例えば、次のようなServiceImp
のモックを作ったとします。
package action
// ServiceImpMock is an implimentation of service
type ServiceImpMock struct {
}
// HelloService return a hello message
func (s ServiceImpMock) HelloService() string {
return "This is test..."
}
ここでは、Hello world!!
の代わりにThis is test...
と返すようにHelloSrvice
の中身が変わっています。
そして、以下のようにmain関数でenv
に渡すインスタンスをモックに変えます。
package main
import (
"sample/action"
"github.com/gin-gonic/gin"
)
func main() {
var service action.ServiceImpMock
env := action.NewEnv(service)
r := gin.Default()
r.GET("/", env.HelloWorld)
r.Run()
}
これで、画面に表示される文字が、”This is test...”へと変わりました。
このように、Env
の中で呼び出されるインスタンを外から渡すようにすることで、挙動を変更することができます。
これを使えばテストを書く際などに、モックを作ってあげることで、インスタンス内で呼び出している他の部品の実装に依存しないテストを作成することができるようになります。
まとめ
DIとは、インスタンス内で使用している他のインスタンスをハードコーディングせずに、外部から渡せるようにすること。
これにより、モックを使うことができるようになり、他の部分の実装に依存しないテストが行えるようになります。