聊一聊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、参考资料
概要
软件测试是一个很宏大的概念,单元测试、集成测试、系统测试、黑盒测试、白盒测试、功能测试、性能测试、基准测试、压力测试等等都是软件测试下面的一种子概念。作为一名开发者,我们并不要理解每一种测试概念,但基准测试和单元测试在软件编写过程中还是必须的,特别是单元测试。
单元测试是指对软件中的最小可测试单元进行检查和验证。至于单元测试的范围并没有一个明确定义,可以是一个函数、方法、类、功能模块或子系统,但在编写时单元测试内容往往是函数或方法。
我们之所以要做单元测试,主要是因为几点:
- 满足可测试性的代码在设计上大概率是良好的(比如函数功能不会包罗万象,导致一个函数上百行代码的地狱场景),从而以较低的成本驱动开发者实现软件编写的高质量;
- 保证软件在迭代过程中质量的稳定性,即一个函数进行优化或功能变化,单元测试可以保证变更后问题及时发现及时解决;
- 有利于后续集成测试、系统测试的稳定推进,想一想开发者把一个不充分自测的软件交付到测试人员手上,是多么可怕的事情,问题进行合理的左移是非常有必要的。
一、测试框架
要想快速的写出优雅的单元测试就必须要了解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
生成代码如下:
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
相关文章:

聊一聊go的单元测试(goconvey、gomonkey、gomock)
文章目录 概要一、测试框架1.1、testing1.2、stretchr/testify1.3、smartystreets/goconvey1.4、cweill/gotests 二、打桩和mock2.1、打桩2.2、mock2.2.1、mockgen2.2.1、示例 三、基准测试和模糊测试3.1、基准测试3.2、模糊测试 四、总结4.1、小结4.2、其他4.3、参考资料 概要…...

Positive Technologies 利用 PT Cloud Application Firewall 保护中小型企业的网络资源
云产品按月订购,无需购买硬件资源 PT Cloud Application Firewall 是 Positive Technologies 推出的首个用于保护网络应用程序的商用云产品。Web 应用层防火墙 (web application firewall, WAF) 现在可以通过 技术合作伙伴——授权服务商和云提供商以订购方式提供1…...

深入解析序列模型:全面阐释 RNN、LSTM 与 Seq2Seq 的秘密
探索序列建模的基础知识和应用。 简介 序列建模是许多领域的一个重要问题,包括自然语言处理 (NLP)、语音识别和语音合成、时间序列预测、音乐生成和「生物信息学」。所有这些任务的共同点是它们需要坚持。接下来的事情的预测是基于历史的。例如,在“哈桑…...

vue项目本地开发构建速度优化 hard-source-webpack-plugin
1、为啥要优化本地构建速度 有些项目因为项目需求点多、功能复杂、管理混乱、引入第三方插件/样式库过多、本身项目页面较多、文件较多等等原因,会导致项目体积变大、本地构建速度明显变慢,这时就需要对项目webpack进行一些设置来提高打包效率、加快打包…...

燕之屋通过港交所聆讯:苦战IPO十余年,黄健等人提前精准套现
撰稿|行星 来源|贝多财经 11月19日,厦门燕之屋生物工程股份有限公司(下称“燕之屋”)通过港交所聆讯,并披露了聆讯后资料集(即招股书),中金公司和广发证券为其联席保荐人。 据贝多财经了解&a…...

【51单片机系列】C51基础
本文内容是关于C51语言的基础内容的,包括C51的数据类型、变量、运算符、函数以及reg52.h文件中的内容,有些与C中相同的内容没有记录在此,比如常量、某些变量、表达式、程序结构、数组等没有涉及。 文章目录 C51的数据类型1. C51中的基本数据类…...

openssl1.0.2版本Windows安装问题
之前安装过1.1版本,Windows环境下C 安装OpenSSL库 源码编译及使用(VS2019)_vs2019安装openssl_肥宝Fable的博客-CSDN博客 后来发现linux编译不过,以为是版本问题,相差太大,所以降一下版本,以免…...

【Java 进阶篇】Ajax 实现——原生JS方式
大家好,欢迎来到这篇关于原生 JavaScript 中使用 Ajax 实现的博客!在前端开发中,我们经常需要与服务器进行数据交互,而 Ajax(Asynchronous JavaScript and XML)是一种用于创建异步请求的技术,它…...

Spring Cloud Stream实践
概述 不同中间件,有各自的使用方法,代码也不一样。 可以使用Spring Cloud Stream解耦,切换中间件时,不需要修改代码。实现方式为使用绑定层,绑定层对生产者和消费者提供统一的编码方式,需要连接不同的中间…...

高精度算法【Java】(待更新中~)
高进度加法 在Java中可以使用BigInteger进行高精度计算,除此也可以仿照竖式相加的计算原理进行计算。 BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算࿱…...

说一说HTTP1.0、1.1、2.0版本区别和优化
说一说HTTP1.0、1.1、2.0版本区别和优化 HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的应用层协议。 在不同的版本中,HTTP经历了一系列的演进和改进,主要包括HTTP 1.0、HTTP 1.1和HTTP 2.0。 下面详细解释它们之间…...

51.Sentinel微服务保护
目录 (1)初识Sentinel。 (1.1)雪崩问题及解决方案。 (1.1.1)雪崩问题。 (1.1.2)解决雪崩问题的四种方式。 (1.1.3)总结。 (1.2)…...

【Java 进阶篇】Ajax 实现——JQuery 实现方式 `ajax()`
嗨,亲爱的读者们!欢迎来到这篇关于使用 jQuery 中的 ajax() 方法进行 Ajax 请求的博客。在前端开发中,jQuery 提供了简便而强大的工具,其中 ajax() 方法为我们处理异步请求提供了便捷的解决方案。无需手动创建 XMLHttpRequest 对象…...

I.MX6ULL开发笔记(一)——环境搭建、镜像烧录、网络连接
本系列为使用野火IMX6ULL开发的学习笔记,使用的开发板为如下: 具有的硬件资源有如下: 文章目录 一、环境搭建Win11安装WSL安装串口驱动安装串口工具安装Ubuntu与windows文件互传 二、镜像烧录修改串口终端登录前信息 三、fire-config工具配…...

Javaweb之Ajax的详细解析
1.1 Ajax介绍 1.1.1 Ajax概述 我们前端页面中的数据,如下图所示的表格中的学生信息,应该来自于后台,那么我们的后台和前端是互不影响的2个程序,那么我们前端应该如何从后台获取数据呢?因为是2个程序,所以…...

java基于RestTemplate的微服务发起http请求
实现的效果...

django理解02 前后端分离中的问题
前后端分离相对于传统方式的问题 前后端数据交换的问题跨域问题 页面js往自身程序(django服务)发送请求,这是浏览器默认接受响应 而请求其它地方是浏览器认为存在潜在危险。自动隔离请求!!! 跨域问题的解决…...

设计模式-迭代器模式-笔记
动机(Motivaton) 在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们呢希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一…...

【数据结构】C语言实现队列
目录 前言 1. 队列 1.1 队列的概念 1.2 队列的结构 2. 队列的实现 2.1 队列的定义 2.2 队列的初始化 2.3 入队 2.4 出队 2.5 获取队头元素 2.6 获取队尾元素 2.7 判断空队列 2.8 队列的销毁 3. 队列完整源码 Queue.h Queue.c 🎈个人主页:…...

牛客——OR36 链表的回文结构(C语言,配图,快慢指针)
目录 思路一:链表翻转 思路二:快慢指针,分别从头和尾间开始比较 本题是没有对C的支持的,但因为CPP支持C,所以这里就用C写了,可以面向更多用户 链表的回文结构_牛客题霸_牛客网 (nowcoder.com) 思路一&am…...

Docker build 技巧 —— 筑梦之路
实现目标 更快的构建速度 更小的Docker镜像大小 更少的Docker镜像层 充分利用镜像缓存 增加Dockerfile可读性 让Docker容器使用起来更简单 如何实现 编写.dockerignore文件 容器只运行单个应用 将多个RUN指令合并为一个 基础镜像的标签不要用latest 每个RUN指令后删除…...

2 Redis的高级数据结构
1、Bitmaps 首先,最经典的应用场景就是用户日活的统计,比如说签到等。 字段串:“dbydc”,根据对应的ASCII表,最后可以得到对应的二进制,如图所示 一个字符占8位(bit),…...

Hive默认分割符、存储格式与数据压缩
目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

update_engine-FilesystemVerifierAction和PostinstallRunnerAction
在介绍完了DownloadAction之后,还剩下FilesystemVerifierAction和PostinstallRunnerAction,下面开始对其进行分析。 FilesystemVerifierAction 在数据下载完成后,在DownloadAction中会切换到FilesystemVerifierAction void DownloadAction:…...

深度学习乳腺癌分类 计算机竞赛
文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度,召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…...

【Python百宝箱】掌握Python Web开发三剑客:Flask、Django、FastAPI一网打尽
前言 在当今互联网时代,Web应用的开发变得愈发重要和复杂。选择一个合适的Web框架,掌握安全性与认证、数据库与ORM库、前端框架与交互、测试与调试工具等关键知识点,是每个Web开发者都必须面对的挑战。本文将带你深入了解三个流行的Python W…...

【人工智能时代的刑法体系与责任主体概述】
第一节:引言 随着科技的快速发展,人工智能 (Artificial Intelligence, AI) 正日益成为我们生活中不可或缺的一部分。从自动驾驶汽车到语音助手,从智能家居到金融机器人,AI 的广泛应用正不断改变着我们的生活方式和社会结构。然而…...

透视maven打包编译正常,intellj idea编译失败问题的本质
前言 maven多模块类型的项目,在Java的中大型应用中非常常见, 在 module 很多的情况,经常会出现各种各样的编辑依赖错误问题,今天记录一种比较常见的 case : A 子模块依赖 B 子模块,在 Terminal 上终端上 …...

npm报错
npm报错 npm ERR! Fix the upstream dependency conflict, or retry npm ERR! this command with --force or --legacy-peer-deps npm ERR! to accept an incorrect (and potentially broken) dependency resolution. npm ERR! npm ERR! npm ERR! For a full report s…...

【FFmpeg实战】ffmpeg播放器-音视频解码流程
音视频介绍 音视频解码流程 FFmpeg解码的数据结构说明 AVFormatContext:封装格式上下文结构体,全局结构体,保存了视频文件封装格式相关信息AVInputFormat:每种封装格式,对应一个该结构体AVStream[0]:视频文件中每个视频ÿ…...