聊一聊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…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
