Adapter Pattern 기반의 Unit Test를 하기 위해서는 기본적인 Unit Test를 왜 해야 하는지 이해해야 한다. 이전 포스팅에 관련된 설명이 있다.
https://namsaenga.tistory.com/62
어댑터 패턴(Adapter Pattern)은 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 패턴이다. 예를 들어, Client는 블록체인에서 특정 블록을 찾기 위해 blockchain 패키지에서 FindBlock() 함수를 호출할 것이다. blockchain 패키지의 FindBlock() 함수는 db 패키지의 FindBlock() 함수를 호출하여 데이터베이스에 접속한 후, 해당 블록을 가져올 것이다. 이러한 구조는 godoc을 이용한 Unit Test를 어렵게 만들기 때문에 어댑터 패턴을 적용하여, 실제 상황에서는 리얼 데이터베이스를 사용하고 테스트 시에는 페이크 데이터베이스를 사용하도록 한다. 또한, 스토리지가 DB가 아닌 USB를 사용해야 할 경우에는 어댑터 패턴을 기반으로 새로운 어댑터를 구현해주기만 하면 된다. 예를 들어, 지금 사용자에게 USB를 스토리지로 제공하고 있지만 USB 인스턴스가 부족하여 DB 사용하려고 한다면, DB의 인스턴스를 참조하는 DBAdapter 클래스를 구현해주면 된다. 사실 사용자는 여유공간만 있으면 되기 때문에 USB를 사용하든 DB를 사용하든 상관이 없다.
<blockchain/blockchain.go>
func persistBlock(b *Block) {
// before: db.SaveBlock(b.Hash, utils.ToBytes(b))
dbStorage.saveBlock(b.Hash, utils.ToBytes(b))
}
func FindBlock(hash string) (*Block, error) {
// before: blockBytes := db.Block(hash)
blockBytes := dbStorage.findBlock(hash)
if blockBytes == nil {
return nil, ErrNotFound
}
block := &Block{}
block.restore(blockBytes)
return block, nil
}
func createBlock(prevHash string, height, diff int) *Block {
block := &Block{
Hash: "",
PrevHash: prevHash,
Height: height,
Difficulty: diff,
Nonce: 0,
}
block.Transactions = Mempool().TxToConfirm()
block.mine()
persistBlock(block)
return block
}
type storage interface {
FindBlock(hash string) []byte
LoadChain() []byte
SaveBlock(hash string, data []byte)
SaveChain(data []byte)
}
var b *blockchain
var once sync.Once
var dbStorage storage = db.DBAdapter{} // storage 인터페이스에 DBAdapter 구조체 할당
func persistBlockhain(b *blockchain) {
// before: db.SaveCheckpoint(utils.ToBytes(b))
dbStorage.saveChain((utils.ToBytes(b)))
}
func Blockchain() *blockchain {
once.Do(func() {
b = &blockchain{
Height: 0,
}
// before: checkpoint := db.Checkpoint()
checkpoint := dbStorage.loadChain()
if checkpoint == nil {
b.AddBlock()
} else {
b.restore(checkpoint)
}
})
return b
}
<db/db.go>
var db *bolt.DB
type DBAdapter struct{}
func (DBAdapter) FindBlock(hash string) []byte {
return findBlock(hash)
}
func (DBAdapter) SaveBlock(hash string, data []byte) {
saveBlock(hash, data)
}
func (DBAdapter) SaveChain(data []byte) {
saveChain(data)
}
func (DBAdapter) LoadChain() []byte {
return loadChain()
}
func InitDB() {
if db == nil {
dbPointer, err := bolt.Open(getDbName(), 0600, nil)
db = dbPointer
utils.HandleErr(err)
err = db.Update(func(t *bolt.Tx) error {
_, err := t.CreateBucketIfNotExists([]byte(dataBucket))
utils.HandleErr(err)
_, err = t.CreateBucketIfNotExists([]byte(blocksBucket))
return err
})
utils.HandleErr(err)
}
}
func findBlock(hash string) []byte {
var data []byte
db.View(func(t *bolt.Tx) error {
bucket := t.Bucket([]byte(blocksBucket))
data = bucket.Get([]byte(hash))
return nil
})
return data
}
'프로그래밍 > GO' 카테고리의 다른 글
[golang] 조건문 있는 함수를 조건문에서 호출하는 유닛 테스트 (0) | 2022.06.13 |
---|---|
[golang] 조건문 및 루프가 있는 함수 유닛 테스트, nested function (0) | 2022.06.09 |
[golang] Adapter Pattern 기반 Unit Test(2) (0) | 2022.06.08 |
[golang] Unit Test 및 Interfaces 기반 테스트 (0) | 2022.06.02 |
godoc 설치 및 사용법 / go 테스트 및 커버리지 확인 (0) | 2022.05.30 |
댓글