본문 바로가기
프로그래밍/GO

[golang] Adapter Pattern 기반 Unit Test(1)

by 남생 namsaeng 2022. 6. 8.
반응형

Adapter Pattern 기반의 Unit Test를 하기 위해서는 기본적인 Unit Test를 왜 해야 하는지 이해해야 한다. 이전 포스팅에 관련된 설명이 있다.


https://namsaenga.tistory.com/62

 

[godoc] Unit Test 및 Interfaces 기반 테스트

이전 포스팅에서 godoc을 이용하여 unit test를 모두 독립적으로 할 수 있다는 것을 알았다. 하지만 코드에 db 및 api 요청이 있을 시에는 쉽게 unit test를 할 수 없다. https://namsaenga.tistory.c..

namsaenga.tistory.com

 


 

[Golang] Adapter Pattern 적용



어댑터 패턴(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
}

 

반응형

댓글