聊一聊go的单元测试(goconvey、gomonkey、gomock)

文章目录

    • 概要
    • 一、测试框架
        • 1.1、testing
        • 1.2、stretchr/testify
        • 1.3、smartystreets/goconvey
        • 1.4、cweill/gotests
    • 二、打桩和mock
        • 2.1、打桩
        • 2.2、mock
            • 2.2.1、mockgen
            • 2.2.1、示例
    • 三、基准测试和模糊测试
        • 3.1、基准测试
        • 3.2、模糊测试
    • 四、总结
        • 4.1、小结
        • 4.2、其他
        • 4.3、参考资料

概要

软件测试是一个很宏大的概念,单元测试、集成测试、系统测试、黑盒测试、白盒测试、功能测试、性能测试、基准测试、压力测试等等都是软件测试下面的一种子概念。作为一名开发者,我们并不要理解每一种测试概念,但基准测试和单元测试在软件编写过程中还是必须的,特别是单元测试。

单元测试是指对软件中的最小可测试单元进行检查和验证。至于单元测试的范围并没有一个明确定义,可以是一个函数、方法、类、功能模块或子系统,但在编写时单元测试内容往往是函数或方法。

我们之所以要做单元测试,主要是因为几点:

  1. 满足可测试性的代码在设计上大概率是良好的(比如函数功能不会包罗万象,导致一个函数上百行代码的地狱场景),从而以较低的成本驱动开发者实现软件编写的高质量;
  2. 保证软件在迭代过程中质量的稳定性,即一个函数进行优化或功能变化,单元测试可以保证变更后问题及时发现及时解决;
  3. 有利于后续集成测试、系统测试的稳定推进,想一想开发者把一个不充分自测的软件交付到测试人员手上,是多么可怕的事情,问题进行合理的左移是非常有必要的。

一、测试框架

要想快速的写出优雅的单元测试就必须要了解Go语言相关的框架,以下是说明:

框架简介
testing内置测试库,用于单元测试、基准测试、模糊测试
cweill/gotests表驱动的测试方式,支持基于模板生成单测,在goland,vs code中都有集成,可以直接使用,提高了单测书写效率
stretchr/testify具体断言机制( testify/require 和 testify/assert),大大简化单元测试的写法,可以替代 t.Fatalf 和 t.Errorf,而且代码实现更为简短、优雅
smartystreets/goconvey提供了丰富的断言机制,相比stretchr/testify,可以使单测看起来更加优雅,并支持断言嵌套

以一个下面的函数对上述框架使用进行说明

func div(a, b int) int {return a / b
}
func add(a, b int) int {return a + b
}
1.1、testing
func TestDiv(t *testing.T) {res := div(10, 2)if res != 1 {t.Error("test div is err")}
}
[root@pigfu ~]# go test -v -run TestDiv
=== RUN   TestDivhelper_test.go:33: test div is err
--- FAIL: TestDiv (0.00s)
FAIL
exit status 1
FAIL    app/test        0.348s
1.2、stretchr/testify
func TestDiv(t *testing.T) {res := div(10, 2)require.Equal(t, 1, res, "test div")
}
[root@pigfu ~]# go test -v -run TestDiv
=== RUN   TestDivhelper_test.go:38:                                                   Error Trace:    F:/WWW/GO/ThinkGin/test/helper_test.go:38Error:          Not equal:                               expected: 1                              actual  : 5                              Test:           TestDiv                                 Messages:       test div is err                                 
--- FAIL: TestDiv (0.00s)                                               
FAIL                                                                     
exit status 1
FAIL    app/test        0.566s

可以看到,编码上更简洁了,友好得写出if xxx { t.Error() } 并且输出更加优美。

ps:注意assert与require区别:require在测试失败以后,所有测试都会停止执行,assert不会

1.3、smartystreets/goconvey
func TestDiv(t *testing.T) {res := div(10, 2)Convey("test div", t, func() {So(res, ShouldEqual, 1)})
}
[root@pigfu ~]# go test -v -run TestDiv
=== RUN   TestDivtest div xFailures:* F:/WWW/GO/ThinkGin/test/helper_test.goLine 44:Expected: '1'Actual:   '5'(Should be equal)1 total assertion--- FAIL: TestDiv (0.00s)
FAIL
exit status 1
FAIL    app/test        0.233s

输出结果相比stretchr/testify更加优美,并且支持断言嵌套。

func TestAll(t *testing.T) {Convey("test div", t, func() {So(div(10, 2), ShouldEqual, 5)Convey("test add", func() {So(add(5, 6), ShouldEqual, 11)})})
}

ps:注意同一个根Convey在第一个断言失败后,其他的测试都会停止执行

func TestAll(t *testing.T) {Convey("test div", t, func() {So(div(10, 2), ShouldEqual, 1)So(add(5, 6), ShouldEqual, 11)Convey("test add", func() {So(add(5, 6), ShouldEqual, 11)})})
}
[root@pigfu ~]# go test -v -run TestAll
=== RUN   TestAlltest div xFailures:* F:/WWW/GO/ThinkGin/test/helper_test.goLine 49:Expected: '1'Actual:   '5'(Should be equal)1 total assertion--- FAIL: TestAll (0.00s)
FAIL
exit status 1
FAIL    app/test        0.293s

可以看到test add并没有被执行,可以采用如下方式避免

func TestAll(t *testing.T) {Convey("test div", t, func() {So(div(10, 2), ShouldEqual, 1)})Convey("test add", t, func() {So(add(5, 6), ShouldEqual, 11)})
}
1.4、cweill/gotests

这个工具可以与前三个做配合,自动生成表驱动的测试代码,其太goland中是有被集成的,鼠标点一下就可以了,非常方便,当然了,也可以命令行执行 gotests -h
goland使用如下:
如下图,鼠标右键点击要单测的函数,可以看到三个选项,依次是只测试这个方法、测试整个文件中方法、测试整个包中的方法,一般选择测试function
goland test
生成代码如下:

func Test_add(t *testing.T) {type args struct {a intb int}tests := []struct {name stringargs argswant int}{// TODO: Add test cases.}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {if got := add(tt.args.a, tt.args.b); got != tt.want {t.Errorf("add() = %v, want %v", got, tt.want)}})}
}

可以看到只需要填充测试数据即可。默认是基于内置测试库生成的,我们也可以基于stretchr/testify或smartystreets/goconvey定义自己的模板。

命令行如下:

[root@pigfu ~]# gotests -h
Usage of D:\gopath\bin\gotests.exe:-all  #对指定文件中的函数和方法都生成测试代码generate tests for all functions and methods-excl string        #要排除的函数或方法regexp. generate tests for functions and methods that don't match. Takes precedence over -only, -exported, and -all-exported #为导出的函数和方法生成go测试,优先级高于 -only和-allgenerate tests for exported functions and methods. Takes precedence over -only and -all-i    print test inputs in error messages #在错误消息中打印测试输入-nosubtests #禁用子测试生成。仅适用于Go 1.7+disable generating tests using the Go 1.7 subtests feature-only string  #指定要单测的函数或方法regexp. generate tests for functions and methods that match only. Takes precedence over -all-template_dir string    #指定模板目录optional. Path to a directory containing custom test code templates-w    write output to (test) files instead of stdout   #指定生成代码的文件,默认stdout

例如:gotests -only div -w herper_test.go helper.go,其他指令自行探索

二、打桩和mock

我们在编写单元测试的时候,如果有第三方依赖怎么办?比如当创建订单的时候,需要写数据库。为了解决这种场景,可以使用打桩或mock的方式,其本质就是能指定依赖方的输入输出,可以理解为提前插入的固定数据,如此,流程就能正常跑起来。

主要试用场景如下:

  • 依赖的服务返回不确定的结果,如获取当前时间;
  • 依赖的服务返回状态中有的难以重建或复现,比如模拟网络错误;
  • 依赖的服务搭建环境代价高,速度慢,需要一定的成本,比如数据库,web服务,RPC服务;
  • 依赖的服务行为多变。
2.1、打桩

打桩简单地来说就是对一些代码片段(全局变量,函数,方法)进行替换。
这种方式主要有两个库prashantv/gostub和agiledragon/gomonkey。前者并不友好,详见本文,这里主要以后者举例:

const DateLayout = "2006-01-02"func StartToday() int64 {date := time.Now().Format(DateLayout)t, _ := time.ParseInLocation(DateLayout, date, time.Local)return t.UnixMilli()
}
func TestStartToday(t *testing.T) {patch := gomonkey.ApplyFunc(time.Now, func() time.Time {return time.Date(2023, 12, 20, 20, 32, 11, 0, time.Local)})//替换time.Now函数defer patch.Reset()//结束后重置time.Now函数Convey("StartToday", t, func() {So(StartToday(), ShouldEqual, 1703001600000)})
}
[root@pigfu ~]# go test -v -run TestStartToday
=== RUN   TestStartTodaystart .1 total assertion--- PASS: TestStartToday (0.03s)
PASS
ok      app/test        0.369s

可以看到 time.Now函数被替换成了 func() time.Time {return time.Date(2023, 12, 20, 20, 32, 11, 0, time.Local)}函数,时间被固定下来了,我们就可以得心应手的写单元测试代码了。

除了ApplyFunc,还有ApplyMethod、ApplyGlobalVar、ApplyFuncSeq等接口,可以自行探索。

2.2、mock

mock通过替换接口来实现对强依赖的处理。
这种方式主要有两个库vektra/mockery和golang/mock。前者是基于stretchr/testify/mock实现的,本文不做过多描述,详见本文。后者是Golang 官方开发维护的测试框架,实现了较为完整的基于 interface 的 Mock 功能,能够与Golang 内置的 testing 包良好集成,也能用于其它的测试环境中。GoMock 测试框架包含了 gomock包和mockgen 工具两部分,其中 gomock 包完成对桩对象生命周期的管理,mockgen 工具用来基于定义的 interface 生成对应的 Mock 类源文件,用来辅助生成测试代码。

二者对比有:

  • stretchr/testify/mock 对应 golang/mock/gomock;
  • vektra/mockery 对应 golang/mock/mockgen 。
2.2.1、mockgen

mockgen 有两种操作模式:源文件模式和反射模式。其命令如下

mockgen 工具支持的选项如下:
-source:指定接口的源文件。-destinatio:mock类代码的输出文件,如果没有设置本选项,代码将被输出到标准输出。-destination选项输入太长,因此推荐使用重定向符号>将输出到标准输出的内容重定向到某个文件,并且mock类代码的输出文件的路径必须是绝对路径。-packag:指定 mock 类源文件的包名,如果没有设置本选项,则包名由 mock_ 和输入文件的包名级联而成。-aux_fi:附加文件列表用于解析嵌套定义在不同文件中的 interface,指定元素列表以逗号分隔,元素形式为foo=bar/baz.go,其中bar/baz.go是源文件,foo是-source选项指定的源文件用到的包名。-build_flags:传递给 build 工具的参数。-imports:依赖的需要 import 的包,在生成的源代码中应该使用的一个显式导入列表,指定为一个以逗号分隔的元素列表,形式为foo=bar/baz,其中bar/baz是被导入的包,foo是生成的源代码中包使用的标识符。-mock_names:自定义生成 mock 文件的列表,使用逗号分割。如 IPay=MockPay,IMan=MockMan。IPay、IMan为接口,MockPay,MockMan为相应的 mock结构体名称 。-self_package:生成代码的完整包导入路径,这个标志的目的是通过尝试包含它自己的包来防止生成代码中的导入死循环。如果 mock 的包被设置为它的一个输入(通常是主输入),并且输出是 stdio,因此 mockgen 无法检测到最终的输出包,就会发生这种情况,设置这个标志将告诉 mockgen 要排除哪个导入。-copyright_file:用于向生成的源代码中添加版权头的版权文件。-debug_parser:只打印解析器结果。-exec_only:(反射模式)如果设置,执行反射程序。-prog_only:(反射模式)只生成反射程序,将其写入stdout并退出。-write_package_comment:如果为true,编写包文档注释(godoc),默认为true。
2.2.1、示例

假设有一个支付接口,依赖第三方的http服务,如下:

type IPay interface {Pay(id string, money float64) errorRefund(id string, money float64) errorQueryPayResult(id string) (float64, error)
}type WxPay struct {Url       stringAppKey    stringAppSecret string
}func (pay *WxPay) sign() string {//签名 sign(AppKey,AppSecret)return "sign result"
}
func (pay *WxPay) Pay(id string, money float64) error {//简单的示例代码,着重强调走了http,依赖第三方服务b, err := json.Marshal(map[string]any{"opr": "pay", "id": id, "money": money})if err != nil {return err}rsp, err := http.Post(pay.Url, "application/json", bytes.NewReader(b))defer func() {_ = rsp.Body.Close()}()return err
}
func (pay *WxPay) Refund(id string, money float64) error {//简单的示例代码,着重强调走了http,依赖第三方服务b, err := json.Marshal(map[string]any{"opr": "refund", "id": id, "money": money})if err != nil {return err}rsp, err := http.Post(pay.Url, "application/json", bytes.NewReader(b))defer func() {_ = rsp.Body.Close()}()return err
}
func (pay *WxPay) QueryPayResult(id string) (float64, error) {//简单的示例代码,着重强调走了http,依赖第三方服务b, err := json.Marshal(map[string]any{"opr": "query_pay_result", "id": id})if err != nil {return 0, err}rsp, err := http.Post(pay.Url, "application/json", bytes.NewReader(b))if err != nil {return 0, err}defer func() {_ = rsp.Body.Close()}()body, err := io.ReadAll(rsp.Body)if err != nil {return 0, err}rspMap := make(map[string]any)err = json.Unmarshal(body, &rspMap)if err != nil {return 0, err}return rspMap["money"].(float64), err
}func GetPayResult(id string, pay IPay) (float64, error) {//业务代码...money, err := pay.QueryPayResult(id)//业务代码...return money, err
}

现在我们要对GetPayResult函数写单元测试,可以这样做:
使用mockgen生成一个对IPay接口mock的结构体,mockgen -destination=./mocks/mock_pay.go -package=mocks -source mock.go -mock_names IPay=MockPay
./mocks/mock_pay.go文件内容如下:

// Code generated by MockGen. DO NOT EDIT.
// Source: mock.go// Package mocks is a generated GoMock package.
package mocksimport (reflect "reflect"gomock "github.com/golang/mock/gomock"
)
// MockPay is a mock of IPay interface.
type MockPay struct {ctrl     *gomock.Controllerrecorder *MockPayMockRecorder
}// MockPayMockRecorder is the mock recorder for MockPay.
type MockPayMockRecorder struct {mock *MockPay
}// NewMockPay creates a new mock instance.
func NewMockPay(ctrl *gomock.Controller) *MockPay {mock := &MockPay{ctrl: ctrl}mock.recorder = &MockPayMockRecorder{mock}return mock
}// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPay) EXPECT() *MockPayMockRecorder {return m.recorder
}// Pay mocks base method.
func (m *MockPay) Pay(id string, money float64) error {m.ctrl.T.Helper()ret := m.ctrl.Call(m, "Pay", id, money)ret0, _ := ret[0].(error)return ret0
}// Pay indicates an expected call of Pay.
func (mr *MockPayMockRecorder) Pay(id, money interface{}) *gomock.Call {mr.mock.ctrl.T.Helper()return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pay", reflect.TypeOf((*MockPay)(nil).Pay), id, money)
}// QueryPayResult mocks base method.
func (m *MockPay) QueryPayResult(id string) (float64, error) {m.ctrl.T.Helper()ret := m.ctrl.Call(m, "QueryPayResult", id)ret0, _ := ret[0].(float64)ret1, _ := ret[1].(error)return ret0, ret1
}// QueryPayResult indicates an expected call of QueryPayResult.
func (mr *MockPayMockRecorder) QueryPayResult(id interface{}) *gomock.Call {mr.mock.ctrl.T.Helper()return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryPayResult", reflect.TypeOf((*MockPay)(nil).QueryPayResult), id)
}// Refund mocks base method.
func (m *MockPay) Refund(id string, money float64) error {m.ctrl.T.Helper()ret := m.ctrl.Call(m, "Refund", id, money)ret0, _ := ret[0].(error)return ret0
}// Refund indicates an expected call of Refund.
func (mr *MockPayMockRecorder) Refund(id, money interface{}) *gomock.Call {mr.mock.ctrl.T.Helper()return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Refund", reflect.TypeOf((*MockPay)(nil).Refund), id, money)
}

现在就引入./mocks/mock_pay.go文件,就可以写单测了。

//go:generate go test -v -run TestMock2
func TestGetPayResult(t *testing.T) {mc := gomock.NewController(t)defer mc.Finish()entry := mocks.NewMockPay(mc)entry.EXPECT().QueryPayResult("123423454345").Return(100.01, nil)Convey("start", t, func() {money, _ := GetPayResult("123423454345", entry)So(money, ShouldEqual, 100.11)})
}

三、基准测试和模糊测试

go test 命令如下:

-convey-json                                                                                                                         When true, emits results in JSON blocks. Default: 'false'                                                                      -convey-silent                                                                                                                       When true, all output from GoConvey is suppressed.                                                                             -convey-story                                                                                                                        When true, emits story output, otherwise emits dot output. When not provided, this flag mirrors the value of the '-test.v' flag-test.bench regexp    run only benchmarks matching regexp                                                                                            -test.benchmem  print memory allocations for benchmarks-test.benchtime d  #基准测试多久(3s)或多少次(300x)run each benchmark for duration d (default 1s)-test.blockprofile filewrite a goroutine blocking profile to file-test.blockprofilerate rateset blocking profile rate (see runtime.SetBlockProfileRate) (default 1)-test.count n  #基准测试多少轮run tests and benchmarks n times (default 1)-test.coverprofile filewrite a coverage profile to file-test.cpu listcomma-separated list of cpu counts to run each test with-test.cpuprofile filewrite a cpu profile to file-test.failfastdo not start new tests after the first test failure-test.fuzz regexprun the fuzz test matching regexp-test.fuzzcachedir stringdirectory where interesting fuzzing inputs are stored (for use only by cmd/go)-test.fuzzminimizetime valuetime to spend minimizing a value after finding a failing input (default 1m0s)-test.fuzztime valuetime to spend fuzzing; default is to run indefinitely-test.fuzzworkercoordinate with the parent process to fuzz random values (for use only by cmd/go)-test.gocoverdir stringwrite coverage intermediate files to this directory-test.list regexplist tests, examples, and benchmarks matching regexp then exit-test.memprofile filewrite an allocation profile to file-test.memprofilerate rateset memory allocation profiling rate (see runtime.MemProfileRate)-test.mutexprofile stringrun at most n tests in parallel (default 4)-test.run regexprun only tests and examples matching regexp-test.shortrun smaller test suite to save time-test.shuffle stringrandomize the execution order of tests and benchmarks (default "off")-test.skip regexpdo not list or run tests matching regexp-test.testlogfile filewrite test action log to file (for use only by cmd/go)-test.timeout dpanic test binary after duration d (default 0, timeout disabled)-test.trace filewrite an execution trace to file-test.v    #列出详情verbose: print additional output
3.1、基准测试

基准测试我们常常来测试一个函数的时间,CPU,内存的使用,来衡量该函数性能是否满足要求

func BinarySearch(n int, f func(int) int) int {i, j := 0, n-1for i <= j {m := int(uint(i+j) >> 1)flag := f(m)if flag == 0 {return m}if flag < 0 {i = m + 1} else {j = m - 1}}return -1
}
func BenchmarkBinarySearch(b *testing.B) {data := []struct {Key string//...}{{Key: "key1"}, {Key: "key2"}, {Key: "key"}, {Key: "key4"}, {Key: "key5"}, {Key: "key6"}, {Key: "key7"}, {Key: "key8"}, {Key: "key9"}, {Key: "key10"},{Key: "key11"}, {Key: "key12"}, {Key: "key13"}, {Key: "key14"}, {Key: "key15"}}for i := 0; i < b.N; i++ {BinarySearch(len(data), func(i int) int {return strings.Compare("key", data[i].Key)})}
}
[root@pigfu ~]# go test -v -bench=BenchmarkBinarySearch -run=none -benchtime=200x #运行200次
goos: linux
goarch: amd64
pkg: app/test
cpu: Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz
BenchmarkBinarySearch
BenchmarkBinarySearch-4              200               137.5 ns/op
PASS
ok      app/test        0.246s
3.2、模糊测试

模糊测试本质是通过枚举参数,不停的调用函数来帮助开发者发现问题,可以进一步保障和提高应用程序的安全性,比如发现边界问题,及时修复,避免panic。在Go1.18彻底加入go的testing库中,之前是一个github项目dvyukov/go-fuzz。
这里以div函数为例,其显然没有考虑到参数b为0的问题,通过模糊测试就可以发现该问题。

func FuzzDiv(f *testing.F) {testcases := []struct {a, b int}{{10, 2},{5, 3},{-6, 3},{-6, -3},}for _, v := range testcases {f.Add(v.a, v.b)}f.Fuzz(func(t *testing.T, a, b int) {fmt.Println(a, b)div(a, b)})
}

或者没有预置参数,这种情况下,不指定时间会无休止的运行,直至panic,比如当b=0的时候。

func FuzzDiv(f *testing.F) {f.Fuzz(func(t *testing.T, a, b int) {div(a, b)})
}

go test -v -fuzz=^FuzzDiv$ -fuzztime=5s

四、总结

4.1、小结

本文总结了Go语言常用的多种测试框架,它们在不同的场景具有不同的应用。个人认为无论什么时候都可以用Convey+So的组合优雅地实现测试用例嵌套和断言,而testify适合最基本的测试(少许测试用例)。gomonkey可以实现对全局变量,函数,方法的替换,gomock作为官方mock库,可以对接口进行很好的替换。因此推荐goconvey、goconvey+gomonkey、goconvey+gomock、goconvey+gomonkey+gomock,这四种组合基本可以覆盖99%的单测场景。

4.2、其他

DATA-DOG/go-sqlmock实现了database/sql/driver,帮助我们做到无数据库测试,符合 TDD 工作流。所有基于 go 标准库database/sql/driver的 orm 框架也都支持,比如 gorm。
alicebob/miniredis是一个实现 Redis Server 的包,专门用于 Go 的单元测试,目前支持 Redis6 的几乎所有开发会用到的命令。
golang官方库net/http/httptest可以解决http的三方依赖。
GRPC 生成的 client stub 都是 Interface,所以可以使用gomock来解决对其的依赖。

4.3、参考资料

1]:Go测试库
2]:Go单元测试:stub与mock
3]:mockery v2的介绍和使用
4]:GoMock框架使用指南
5]:Go语言工具包之gomock

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/152299.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

六.Linux远程登录

1.说明&#xff1a;公司开发的时候&#xff0c;具体的应用场景是这样的 1.linux服务器是开发小组共享 2.正式上线的项目是运行在公网 3.因此程序员需要远程登录到Linux进行项目管理或者开发 4.画出简单的网络拓扑示意图(帮助理解) 5.远程登录客户端有Xshell6、Xftp6&#xff0…

7年经验之谈 —— 如何高效的开展app的性能测试?

APP性能测试是什么 从网上查了一下&#xff0c;貌似也没什么特别的定义&#xff0c;我这边根据自己的经验给出一个自己的定义&#xff0c;如有巧合纯属雷同。 客户端性能测试就是&#xff0c;从业务和用户的角度出发&#xff0c;设计合理且有效的性能测试场景&#xff0c;制定…

3D建模基础教程:石墨工具介绍

3DMAX的石墨&#xff08;Graphite&#xff09;工具是一个强大的建模工具&#xff0c;可以用来创建和编辑复杂的3D模型。下面是对石墨工具的详细介绍&#xff1a; 石墨工具的界面布局&#xff1a; 石墨工具的界面与3DMAX的主界面相同&#xff0c;包括菜单栏、工具栏、视图区、…

【LeetCode】二叉树OJ

目录 一、根据二叉树创建字符串 二、二叉树的层序遍历 三、二叉树的层序遍历 II 四、二叉树的最近公共祖先 五、二叉搜索树与双向链表 六、从前序与中序遍历序列构造二叉树 七、从中序与后序遍历序列构造二叉树 一、根据二叉树创建字符串 606. 根据二叉树创建字符串 - …

redis运维(八)数据类型(一)字符串

一 字符串 说明&#xff1a; 不需要精通,但是得有一个粗略的认识,然后利用help command查看具体使用仅做记录查询 ① 基础概念 说明&#xff1a; ex是用来收敛内存使用率备注&#xff1a; 早期set是不带ex的默认&#xff1a; 不设置ex,是常驻内存 key和value的命名规范 …

Java-类和类的关系

代码 总结&#xff1a; 【1】面向对象的思维&#xff1a;找参与者&#xff0c;找女孩类&#xff0c;找男孩类 【2】体会了什么叫方法的形参&#xff0c;什么叫方法的实参&#xff1a; 具体传入的内容 实参&#xff1a; 【3】类和类可以产生关系&#xff1a; &#xff08;1…

基于非洲秃鹫算法优化概率神经网络PNN的分类预测 - 附代码

基于非洲秃鹫算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于非洲秃鹫算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于非洲秃鹫优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

电容的耐压值是什么意思呢?

电容是什么&#xff1f; 电容是一种能以电荷的形式储存能量的装置。与同样大小的电池相比&#xff0c;电容能储存的能量要小得多&#xff0c;大约1w个电容存储的能量才顶一节电池存储的能量&#xff0c;但对于许多电路设计来说却足够使用了。 看下图的直插式电容&#xff0c;…

中级程序员——uniapp和小程序面试题

&#x1f604;博主&#xff1a;小猫娃来啦 &#x1f604;文章核心&#xff1a;uniapp和小程序面试题 文章目录 用uniapp有遇到一些兼容性问题吗&#xff1f;uniapp最大的优点是什么&#xff1f;uniapp如何实现多端兼容&#xff1f;uniapp是如何做跨端适配的&#xff1f;常用的u…

信息中心网络提出的背景、研究现状及研究内容

信息中心网络什么时候提出的&#xff1f;未来发展前景&#xff1f;有什么著名实验室在做&#xff1f; 1、提出背景&#xff1a; 互联网产生于上世纪60年代&#xff1a; &#xff08;1&#xff09;网络设备数量呈指数性增长 截至2022年底全球范围内预计将有超过280亿台终端设…

YOLO目标检测——无人机航拍输电线路绝缘瓷瓶数据集下载分享【对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;电力系统运维、状态监测与故障诊断、智能电网建设等领域数据集说明&#xff1a;无人机航拍输电线路绝缘瓷瓶数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标注框质量…

【心得】基于flask的SSTI个人笔记

目录 计算PIN码 例题1 SSTI的引用链 例题2 SSTI利用条件&#xff1a; 渲染字符串可控&#xff0c;也就说模板的内容可控 我们通过模板 语法 {{ xxx }}相当于变相的执行了服务器上的python代码 利用render_template_string函数参数可控&#xff0c;或者部分可控 render_…

ThreadLocal优化

测试类证明一下ThreadLocal存储的数据是线程程安全的 package com.lin.springboot01;import org.junit.jupiter.api.Test;public class testThreadLocal {Testpublic void testThreadLocalSetAndGet(){//提供一个ThreadLocal对象ThreadLocal t1 new ThreadLocal();new Thread…

【SQL server】数据库、数据表的创建

创建数据库 --如果存在就删除 --所有的数据库都存在sys.databases当中 if exists(select * from sys.databases where name DBTEST)drop database DBTEST--创建数据库 else create database DBTEST on --数据文件 (nameDBTEST,--逻辑名称 字符串用单引号filenameD:\DATA\DBT…

Linux 基本语句_11_无名管道文件复制

父子进程&#xff1a; 父子进程的变量之间存在着读时共享&#xff0c;写时复制原则 无名管道&#xff1a; 无名管道仅能用于有亲缘关系的进程之间通信如父子进程 代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <sys/types.h> #inc…

数据结构【DS】图的基本概念

定义 完全图(简单完全图) 完全无向图&#xff1a;边数为&#x1d427;&#x1d427;−&#x1d7cf;&#x1d7d0;完全有向图&#xff1a;边数为 &#x1d427;(&#x1d427;−&#x1d7cf;) 子图、生成子图 G的子图&#xff1a;所有的顶点和边都属于图G的图 G的生成子图…

数据结构与算法编程题4

删除线性表中值在s与t之间的元素&#xff08;s<t&#xff09;&#xff0c;s和t不合理或者顺序表为空则显示出错信息并退出运行。 #include <iostream> using namespace std;typedef int ElemType; #define Maxsize 100 #define OK 1 #define ERROR 0 typedef struct…

SIMULIA 2022 Abaqus新功能之非线性、工作流、子程序、Explicit等

Abaqus 非线性力学的功能增强 Valanis-Landel 超弹性材料 通过指定单轴试验数据和可选的体积试验数据&#xff08;v2022新增选项&#xff09;来定义Valanis-Landel 超弹性模型&#xff0c;该模型能精确地复现给定的数据&#xff0c;类似Marlow模型&#xff0c;但与Marlow模型的…

深信服AC设备用户认证

拓扑图 目录 拓扑图 一. 无需认证 思路&#xff1a;创建用户和组&#xff0c;将无需认证策略和用户绑定 1.创建组&#xff0c;组里添加用户 2. 新建不需要认证策略&#xff0c;将不需要认证策略和用户关联 3.验证 二.密码认证 思路&#xff1a;创建用户和组&#xff0c;并…

在Vue关于ue的computed属性中传递参数

computed的基本用法 computed是Vue实例中一个非常强大的计算属性&#xff0c;它的值主要根据依赖数据而变化。我们可以将一些简单的计算放在computed属性中&#xff0c;从而实现在模板中使用简单的表达式。 但是实际项目中通常有这么一个场景&#xff1a;根据传递不一样的参数值…