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.