为了我的测试目的,我想在Go中模拟随机数字。所以我创建了Random接口。在单元测试期间,我返回标识函数,而对于实现,我使用rand包生成一个随机数。
这是在Go中模拟随机数的正确方法吗?任何帮助表示赞赏。
去游乐场:https://play.golang.org/p/bibNnmY2t1g
主:
package main
import (
"time"
"math/rand"
"fmt"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type Random interface {
Uint(_ uint) uint
}
type rndGenerator func(n uint) uint
type Randomizer struct {
fn rndGenerator
}
func NewRandomizer(fn rndGenerator) *Randomizer {
return &Randomizer{fn: fn}
}
func (r *Randomizer) Uint(n uint) uint {
return r.fn(n)
}
func fakeRand(n uint) uint { return n }
func realRand(_ uint) uint { return uint(rand.Uint64()) }
func main() {
fakeRnd := NewRandomizer(fakeRand).Uint
fmt.Println(fakeRnd(1))
fmt.Println(fakeRnd(2))
realRnd := NewRandomizer(realRand).Uint
fmt.Println(realRnd(0))
fmt.Println(realRnd(0))
}
试验:
package main
import (
"testing"
"math/rand"
"reflect"
)
func TestNewRandomizer(t *testing.T) {
fn := func(n uint) uint { return n }
type args struct {
fn rndGenerator
}
tests := []struct {
name string
args args
want *Randomizer
}{
{
"test",
args{fn},
&Randomizer{fn},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewRandomizer(tt.args.fn); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
t.Errorf("NewRandomizer() = %v, want %v", got, tt.want)
}
})
}
}
func TestRandomer_Uint(t *testing.T) {
rnd := uint(rand.Uint64())
type fields struct {
fn rndGenerator
}
type args struct {
n uint
}
tests := []struct {
name string
fields fields
args args
want uint
}{
{
"test",
fields{func(n uint) uint { return n }},
args{rnd},
rnd,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Randomizer{
fn: tt.fields.fn,
}
if got := r.Uint(tt.args.n); got != tt.want {
t.Errorf("Randomizer.Uint() = %v, want %v", got, tt.want)
}
})
}
}
func Test_fakeRand(t *testing.T) {
rnd := uint(rand.Uint64())
type args struct {
n uint
}
tests := []struct {
name string
args args
want uint
}{
{
"test",
args{rnd},
rnd,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := fakeRand(tt.args.n); got != tt.want {
t.Errorf("fakeRand() = %v, want %v", got, tt.want)
}
})
}
}
func Test_realRand(t *testing.T) {
type args struct {
in0 uint
}
tests := []struct {
name string
args args
want bool
}{
{
"test",
args{0},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := realRand(tt.args.in0); got < 1 {
t.Errorf("realRand() = %v, want %v", got, tt.want)
}
})
}
}
答案 0 :(得分:0)
您的示例没有真正使用Random
接口,因为模拟是在Randomizer
类型的函数字段级别完成的。
如果可能的话,我建议抛弃函数字段和函数,而是定义Random
接口的两个独立实现。你可以使用空结构,它们起初可能看起来很奇怪,但它们具有占用0字节内存的良好属性。
建议的主要原因是当您使用函数字段时,您将无法将结构类型值与reflect.DeepEqual
进行比较。这是因为如果两个函数值具有相同类型且两者都是nil ,则它们只相等。
作为一个例子,让我们首先看看TestNewRandomizer
,这是上述问题的症状。在测试中,您只是比较返回值的类型,这是编译器已经确保的,因此测试绝对没有意义。
现在,让我们说你放弃测试,因为它没用,但由于某种原因你保留了功能字段。因此,任何依赖于*Randomizer
的结构类型对于DeepEqual
也是不可测试的,并且在尝试为该类型进行测试时会遇到同样的困难。
package main
import (
"time"
"math/rand"
"fmt"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type Random interface {
Uint(_ uint) uint
}
type Randomizer struct {}
func NewRandomizer() Randomizer {
return Randomizer{}
}
func (r Randomizer) Uint(n uint) uint {
return uint(rand.Uint64())
}
type FakeRandomizer struct {}
func NewFakeRandomizer() FakeRandomizer {
return FakeRandomizer{}
}
func (r FakeRandomizer) Uint(n uint) uint {
return n
}
func main() {
fakeRnd := NewFakeRandomizer().Uint
fmt.Println(fakeRnd(1))
fmt.Println(fakeRnd(2))
realRnd := NewRandomizer().Uint
fmt.Println(realRnd(0))
fmt.Println(realRnd(0))
}
请注意,我故意返回值而不是指针,因为空结构比指针小。
答案 1 :(得分:0)
我有一个生成随机整数的方法,如果该整数小于或等于50,则返回true
;如果该整数大于[50,100],则返回false
。
这是我创建结构以模拟功能的方式:
type Decider struct {
RandImpl func(int) int
}
func (d *Decider) Decide(randRange int) bool {
randVal := d.RandImpl(randRange)
log.Info("RandVal: ", randVal)
if randVal <= 50 {
return true
}
return false
}
我以这种方式调用此方法:
rand.Seed(time.Now().UnixNano())
decider := decide.Decider{
RandImpl: func(x int) int { return rand.Intn(x) },
}
decider.Decide(100)
在我的_test.go
文件中,我有这个:
decider := Decider{
RandImpl: func(x int) int { return 42 },
}
ret := decider.Decide(100)
assert.True(t, ret)
通过这种方式,您可以模拟随机数生成器的功能。