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

[golang] 조건문 있는 함수를 조건문에서 호출하는 유닛 테스트

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

이전 포스팅을 통해 조건문 및 루프가 있는 함수의 유닛 테스트를 알아보았다. 이번에는 조건문이 있는 함수를 조건문에서 호출하는 함수의 유닛 테스트이다.

 

 

https://namsaenga.tistory.com/70

 

[go test] 조건문 및 루프가 있는 함수 유닛 테스트, nested function

golang 유닛 테스트를 하다 보면 대상 함수에 단순히 조건문뿐만 아니라 루프가 있는 경우도 있다. 루프 안에 조건문에 대한 커버리지를 확인하기 위해서는 테스트 함수에서 루프를 빠져나갈 상

namsaenga.tistory.com

 

 


 

 

조건문 있는 함수를 조건문에서 호출하는 유닛 테스트

getDifficulty 함수의 유닛 테스트는 조건문에 따라 3가지의 경우(b.Height가 0, b.Height%difficultyInterval이 0, 그 외)를 고려해야 한다.  b.Height%difficultyInterval이 0일 때는 recalculateDifficulty 함수를 호출하는데 이 함수 내에도 조건문이 존재해 3가지의 경우를 다 테스트해야 한다. TestRecalculateDifficulty(t *testing.T) 함수가 정의되어 있다면 테스트를 위해 recalculateDifficulty(b *blockchain) int를 호출하는 것이 아닌 TestRecalculateDifficulty(t *testing.T) 함수를 호출할 것이기 때문에, 해당 함수에 3개의 커버리지를 테스트할 수 있는 서브 테스트를 만들어 준다.

 

func getDifficulty(b *blockchain) int {

	if b.Height == 0 {
		return defaultDifficulty
	} else if b.Height%difficultyInterval == 0 {
		return recalculateDifficulty(b)
	} else {
		return b.CurrentDifficulty
	}
}

func recalculateDifficulty(b *blockchain) int {
        allBlocks := Blocks(b)
        newestBlock := allBlocks[0]                              // 가장 최근 블록은 allBlocks[0]
        lastRecalculatedBlock := allBlocks[difficultyInterval-1] // 가장 최근 재설정된 블록
        actualTime := (newestBlock.Timestamp / 60) - (lastRecalculatedBlock.Timestamp / 60)
        expectedTime := difficultyInterval * blockInterval
        if actualTime < (expectedTime - allowedRange) {
                return b.CurrentDifficulty + 1
        } else if actualTime > (expectedTime + allowedRange) {
                return b.CurrentDifficulty - 1
        }
        return b.CurrentDifficulty
}

 

여기에서 첫 번째 t.Run은 recalculateDifficulty의 if actualTime < (expectedTime - allowedRange) 를 테스트하고, 두 번째 t.Run은 else, 마지막 t.Run은 else if actualTime > (expectedTime + allowedRange) 테스트한다.

 

func TestRecalculateDifficulty(t *testing.T) {
	t.Run("1", func(t *testing.T) {
		blocks := []*Block{
			{PrevHash: "x", Timestamp: 120},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "", Timestamp: 0},
		}
		fakeBlock := 0
		dbStorage = fakeDB{
			fakeFindBlock: func() []byte {
				defer func() {
					fakeBlock++
				}()
				return utils.ToBytes(blocks[fakeBlock])
			},
		}
		bc := &blockchain{} //{Height: 5, CurrentDifficulty: 3}
		got := recalculateDifficulty(bc)
		t.Errorf(" recalculateDifficulty should return %d got %d", bc.CurrentDifficulty+1, got)

	})
	t.Run("2", func(t *testing.T) {
		blocks := []*Block{
			{PrevHash: "x", Timestamp: 480},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "", Timestamp: 0},
		}
		fakeBlock := 0
		dbStorage = fakeDB{
			fakeFindBlock: func() []byte {
				defer func() {
					fakeBlock++
				}()
				return utils.ToBytes(blocks[fakeBlock])
			},
		}
		bc := &blockchain{Height: 5, CurrentDifficulty: 3}
		got := recalculateDifficulty(bc)
		t.Errorf(" recalculateDifficulty should return %d got %d", bc.CurrentDifficulty, got)

	})
	t.Run("3", func(t *testing.T) {
		once = *new(sync.Once)
		blocks := []*Block{
			{PrevHash: "x", Timestamp: 4800},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "x", Timestamp: 60},
			{PrevHash: "", Timestamp: 0},
		}
		fakeBlock := 0
		dbStorage = fakeDB{
			fakeFindBlock: func() []byte {
				defer func() {
					fakeBlock++
				}()
				return utils.ToBytes(blocks[fakeBlock])
			},
		}
		bc := &blockchain{} //{Height: 5, CurrentDifficulty: 3}
		got := recalculateDifficulty(bc)
		t.Errorf(" recalculateDifficulty should return %d got %d", bc.CurrentDifficulty-1, got)
	})
}

func TestGetDifficulty(t *testing.T) {
	blocks := []*Block{
		{PrevHash: "x", Timestamp: 720},
		{PrevHash: "x", Timestamp: 60},
		{PrevHash: "x", Timestamp: 60},
		{PrevHash: "x", Timestamp: 60},
		{PrevHash: "", Timestamp: 0},
	}
	fakeBlock := 0
	dbStorage = fakeDB{
		fakeFindBlock: func() []byte {
			defer func() {
				fakeBlock++
			}()
			return utils.ToBytes(blocks[fakeBlock])
		},
	}
	type test struct {
		height int
		want   int
	}
	tests := []test{
		{height: 0, want: defaultDifficulty},
		{height: 2, want: defaultDifficulty},
		{height: 5, want: 3},
	}
	for _, tc := range tests {
		bc := &blockchain{Height: tc.height, CurrentDifficulty: defaultDifficulty}
		got := getDifficulty(bc)
		if got != tc.want {
			t.Errorf("getDifficulty() should return %d got %d", tc.want, got)
		}
	}
}

 

반응형

댓글