Due to the fast layout of prototype, the init. engineer(i.e tactical tornado style) who wrote the code didn't have unit testing in mind(in golang the idiom's bit different), the team will refactor the code a bit in order to fill the holes of unit tests.
Before refactoring, there are two ground baseline to be considered.
1. minimum change of the code thus to support interface unit test.
2. honor golang interface idiom.
Here are some good examples we found in etcd source code for unit test layout :-)
https://github.com/coreos/etcd/blob/master/etcdserver/raft.go
https://github.com/coreos/etcd/blob/master/etcdserver/raft_test.go
We adapted embedded interface and CRTP style for grouping functions into interface as the minimum element of unit-testing.
e.g code:
https://github.com/buddhavs/golang_unit_test_example
example.go
Before refactoring, there are two ground baseline to be considered.
1. minimum change of the code thus to support interface unit test.
2. honor golang interface idiom.
Here are some good examples we found in etcd source code for unit test layout :-)
https://github.com/coreos/etcd/blob/master/etcdserver/raft.go
https://github.com/coreos/etcd/blob/master/etcdserver/raft_test.go
We adapted embedded interface and CRTP style for grouping functions into interface as the minimum element of unit-testing.
e.g code:
https://github.com/buddhavs/golang_unit_test_example
example.go
package main
import (
"errors"
"fmt"
)
type (
// Service ...
Service struct {
Mq
Storage
Status
}
// Mq ...
Mq interface {
process(bool) error
}
// Storage ...
Storage interface {
snapshot() error
backup() error
}
// Status ...
Status interface {
update()
updatedb()
check()
}
service struct {
s *Service
}
mq struct {
service
}
storage struct {
service
}
status struct {
service
}
)
// NewService ...
func NewService() *Service {
mq := &mq{}
storage := &storage{}
status := &status{}
service := Service{
Mq: mq,
Storage: storage,
Status: status}
mq.s = &service
storage.s = &service
status.s = &service
return &service
}
func (p *mq) process(b bool) error {
fmt.Println("Processing...")
err := p.s.backup()
if err != nil {
fmt.Println("Backup error...")
}
err = p.s.snapshot()
if err != nil {
fmt.Println("Snapshot error...")
}
switch b {
case true:
return nil
default:
return errors.New("Process error")
}
}
func (p *storage) backup() error {
fmt.Println("Backing up...")
p.s.update()
p.s.updatedb()
p.s.check()
return nil
}
func (p *storage) snapshot() error {
fmt.Println("Snap shotting XD...")
return nil
}
func (p *status) update() {
fmt.Println("Updaing status...")
}
func (p *status) updatedb() {
fmt.Println("Updaing Db...")
}
func (p *status) check() {
fmt.Println("Checking status...")
}
func main() {
service := NewService()
service.process(true)
}
example_test.go
package main
import "testing"
// NewMockService ...
func NewMockService() *Service {
mq := &mq{}
storage := &mockStorage{}
status := &mockStatus{}
service := Service{
Mq: mq,
Storage: storage,
Status: status}
mq.s = &service
storage.s = &service
status.s = &service
return &service
}
type (
mockStorage struct {
service
}
mockStatus struct {
service
}
)
func (p *mockStorage) backup() error {
return nil
}
func (p *mockStorage) snapshot() error {
return nil
}
func (p *mockStatus) update() {
}
func (p *mockStatus) updatedb() {
}
func (p *mockStatus) check() {
}
func TestStatusExpectFailed(t *testing.T) {
service := NewMockService()
err := service.process(false)
if err != nil {
t.Error(err)
}
}
func TestStatus(t *testing.T) {
service := NewMockService()
err := service.process(true)
if err != nil {
t.Error(err)
}
}
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.