Go 中的模拟随机生成器

2024-01-27

出于测试目的,我想在 Go 中模拟随机数。所以我创建了随机界面。在单元测试期间,我返回恒等函数,而在实现过程中,我使用 rand 包生成一个随机数。

这是在 Go 中模拟随机数的正确方法吗?任何帮助表示赞赏。

去游乐场:https://play.golang.org/p/bibNnmY2t1g https://play.golang.org/p/bibNnmY2t1g

main:

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))
}

test:

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)
            }
        })
    }
}

您的示例没有真正使用Random接口,因为模拟是在函数字段级别完成的Randomizer type.

如果可能的话,我建议放弃函数字段和函数,而是定义两个单独的实现Random界面。您可以为此使用空结构,它们一开始可能看起来很奇怪,但它们具有占用 0 字节内存的良好属性。

建议的主要原因是,当您使用函数字段时,您将无法将结构类型值与reflect.DeepEqual。这是因为两个函数值仅在具有相同类型时才相等两者都为零.

作为一个例子,让我们首先看看你的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))
}

请注意,我故意返回值而不是指针,因为空结构比指针小。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go 中的模拟随机生成器 的相关文章

随机推荐