当前位置: 首页 > wzjs >正文

上海专业做网站公司电话搜狗搜索网页版

上海专业做网站公司电话,搜狗搜索网页版,网站建设费如何记账,香港外贸网站建设背景在实际开发过程中,我们常常面临这样的场景:当新增需求只是在原有逻辑基础上增加一小部分功能时,全面的接口测试显得冗余;而复杂业务逻辑的测试又面临造数据难、并发场景模拟复杂等问题。此时,单元测试的优势便凸显…

背景

在实际开发过程中,我们常常面临这样的场景:当新增需求只是在原有逻辑基础上增加一小部分功能时,全面的接口测试显得冗余;而复杂业务逻辑的测试又面临造数据难、并发场景模拟复杂等问题。此时,单元测试的优势便凸显出来。

然而,单元测试中频繁的数据库操作会带来新的困扰:测试用例与数据库数据强耦合,不仅需要专门构造测试数据,还可能因数据库数据变更导致测试用例失效,增加问题定位成本。针对这些痛点,本文将介绍一种基于 Golang 特性的解决方案。

解决方案:利用 Golang 函数特性实现 Mock

Golang 中,函数被视为 "一等公民"—— 这意味着函数与变量具有同等地位,可以像变量一样作为参数传递、赋值或存储。正是这一特性,为我们提供了一种简洁高效的 Mock 实现方式。

核心实现思路

我们可以将数据库操作封装为函数变量,在业务代码中通过这些变量调用数据库方法;而在单元测试时,重新赋值这些函数变量,从而替代实际的数据库操作。

例如,定义如下函数变量用于数据库查询:

var GetRecordCountByCardId = func(ctx context.Context, giftCardId string, event int) (int64, error) {return new(models.GiftCardLogs).GetRecordCountByCardId(ctx, giftCardId, event)
}

在单元测试中,我们可以这样重新赋值以模拟数据库查询结果: 

   GetRecordCountByCardId = func(ctx context.Context, cardNum string, event int) (int64, error) {return 2, nil}

通过这种方式,单元测试无需执行实际的数据库查询,彻底消除了与数据库数据的耦合。

完整示例代码 

package gift_card_serviceimport ("context""sync""testing""github.com/stretchr/testify/assert"
)// 测试正常发生告警场景
func TestSendDuplicateActivetaAlert_Sucess(t *testing.T) {ctx := context.Background()ctx = context.WithValue(ctx, "trace_id", "test_trace_123456")cardNum := "test_12345"customerId := int64(37856993)event := 3//mock操作GetRecordCountByCardId = func(ctx context.Context, cardNum string, event int) (int64, error) {return 2, nil}//mock操作GetCustomerById = func(ctx context.Context, customerId int64) (models.Customer, error) {return models.Customer{Phone: "13812345678",}, nil}err := sendDuplicateActivateAlert(ctx, customerId, cardNum, event)if err != nil {assert.Error(t, err)}
}// 测试并行运行
// 模拟两次从数据库查询获得的记录,一次是1,一次是2
// 预期结果:只发送一次告警
func TestSendDuplicateActivateAlert_Concurrency(t *testing.T) {ctx := context.Background()ctx = context.WithValue(ctx, "trace_id", "test_concurrency_hit")cardNum := "test_concurrency_123"customerId := int64(37856994)event := 3//测试并发执行,只发一次告警,起两个协程并发执行wg := sync.WaitGroup{}wg.Add(2)go func() {//mock操作GetRecordCountByCardId = func(ctx context.Context, cardNum string, event int) (int64, error) {return 1, nil}//mock操作GetCustomerById = func(ctx context.Context, customerId int64) (models.Customer, error) {return models.Customer{Phone: "13812345678",}, nil}defer wg.Done()err := sendDuplicateActivateAlert(ctx, customerId, cardNum, event)assert.NoError(t, err)}()go func() {//mock操作GetRecordCountByCardId = func(ctx context.Context, cardNum string, event int) (int64, error) {return 2, nil}//mock操作GetCustomerById = func(ctx context.Context, customerId int64) (models.Customer, error) {return models.Customer{Phone: "13812345678",}, nil}defer wg.Done()err := sendDuplicateActivateAlert(ctx, customerId, cardNum, event)assert.NoError(t, err)}()wg.Wait()
}// 测试并行运行
// 模拟两次从数据库查询获得的记录都是2的情况
// 期望只发送一次告警
func TestSendDuplicateActivateAlert_Concurrency_RecordCountEqual2(t *testing.T) {ctx := context.Background()ctx = context.WithValue(ctx, "trace_id", "test_concurrency_recordcountequal2")cardNum := "test_concurrency_recordcountequal2_123"customerId := int64(37856994)event := 3//测试并发执行,只发一次告警,起两个协程并发执行wg := sync.WaitGroup{}wg.Add(2)go func() {//mock操作GetRecordCountByCardId = func(ctx context.Context, cardNum string, event int) (int64, error) {return 2, nil}//mock操作GetCustomerById = func(ctx context.Context, customerId int64) (models.Customer, error) {return models.Customer{Phone: "13812345678",}, nil}defer wg.Done()err := sendDuplicateActivateAlert(ctx, customerId, cardNum, event)assert.NoError(t, err)}()go func() {//mock操作GetRecordCountByCardId = func(ctx context.Context, cardNum string, event int) (int64, error) {return 2, nil}//mock操作GetCustomerById = func(ctx context.Context, customerId int64) (models.Customer, error) {return models.Customer{Phone: "13812345678",}, nil}defer wg.Done()err := sendDuplicateActivateAlert(ctx, customerId, cardNum, event)assert.NoError(t, err)}()wg.Wait()
}// 测试激活记录数不足场景
func TestSendDuplicateActivateAlert_RecordCountLessThan2(t *testing.T) {ctx := context.Background()ctx = context.WithValue(ctx, "trace_id", "test_trace_count_less")cardNum := "test_count_less_456"customerId := int64(37856995)event := 3GetRecordCountByCardId = func(ctx context.Context, cardNum string, event int) (int64, error) {return 1, nil // 记录数小于2}//mock操作GetCustomerById = func(ctx context.Context, customerId int64) (models.Customer, error) {return models.Customer{Phone: "13812345678",}, nil}// 执行测试err := sendDuplicateActivateAlert(ctx, customerId, cardNum, event)// 断言结果assert.NoError(t, err)
}
func isSendAlert(ctx context.Context, cardNum string) (bool, error) {redisKey := cache.GiftCard[fmt.Sprintf(cache.GiftCard["duplicate_alert"], cardNum)]id, err := redis.NewIncrement(redisKey, 30*time.Second).GetIncrementID(ctx)if err != nil {// 失败了,发送告警,宁可重复发送,不要漏掉发送return false, err}if id > 1 {return true, nil}return false, nil
}// 主要是为了测试用例中,mock操作,屏蔽数据库操作
var GetRecordCountByCardId = func(ctx context.Context, giftCardId string, event int) (int64, error) {return new(models.GiftCardLogs).GetRecordCountByCardId(ctx, giftCardId, event)
}// 主要是为了测试用例中,mock操作,屏蔽数据库操作
var GetCustomerById = func(ctx context.Context, customerId int64) (models.Customer, error) {return models.GetCustomerById(ctx, customerId)
}// 发送重复激活告警
func sendDuplicateActivateAlert(ctx context.Context, customerId int64, cardNum string, event int) error {// 1. 查询礼品卡激活记录数count, err := GetRecordCountByCardId(ctx, cardNum, event)if err != nil {return err}log.InfoWithCtx(ctx, "查询礼品卡激活记录数结果", zap.String("cardNum", cardNum), zap.Int64("count", count))if count < 2 {return nil}// 2. 检查并设置告警缓存,防止重复发送isSend, err := isSendAlert(ctx, cardNum)if err != nil {log.ErrorWithCtx(ctx, "检查重复激活告警缓存失败", zap.String("cardNum", cardNum), zap.Error(err))}if isSend {log.InfoWithCtx(ctx, "重复激活告警已发送过,跳过本次发送", zap.String("cardNum", cardNum))return nil}// 3. 根据customerId查询手机信息var customerInfo stringcustomer, err := GetCustomerById(ctx, customerId)if err != nil {log.ErrorWithCtx(ctx, "查询用户信息失败", zap.Int64("customerId", customerId), zap.Error(err))customerInfo = strconv.FormatInt(customerId, 10)} else {customerInfo = customer.Phone}// 4. 拼接告警模板alarmTime := time.Now().Format("2006-01-02 15:04:05")msg := fmt.Sprintf(`事务名称:礼品卡异常异常内容:礼品卡重复激活异常账号:%s告警时间:%s其他信息:卡号:%s,重复激活次数:%d`,customerInfo, alarmTime, cardNum, count)// 5. 调用超紧急告警接口(使用传入的ctx而非新创建的上下文)if err := qiyewx_robot_service.QiWeiUrgentAlarm(ctx, msg); err != nil {return err}log.InfoWithCtx(ctx, "重复激活告警已成功发送", zap.String("cardNum", cardNum), zap.String("customerInfo", customerInfo))return nil
}

方案优势

  1. 解耦数据库依赖:单元测试不再依赖实际数据库,避免因数据变化导致测试用例失效。

  2. 简化测试数据准备:无需构造复杂的数据库数据,通过 Mock 函数直接返回预设结果。

  3. 提升测试效率:省去数据库交互开销,大幅加快单元测试执行速度。

  4. 覆盖特殊场景:轻松模拟并发、异常等难以通过接口测试复现的场景。

  5. 保持代码简洁:无需引入复杂的 Mock 框架,利用 Golang 原生特性即可实现 Mock 功能。

这种方案特别适合业务逻辑复杂、数据库操作频繁的场景,既能保证单元测试的独立性和稳定性,又能降低测试维护成本,是 Golang 项目中值得推广的单元测试实践方式。

http://www.dtcms.com/wzjs/521259.html

相关文章:

  • 网站建设兼职劳务协议视频号推广方法
  • 网站开发与管理对应的职业及岗位长沙官网seo收费
  • 网站开发实训心得简述网络营销与传统营销的整合
  • 网站建设需要什么软件有哪些众志seo
  • 自建网站备案山东seo百度推广
  • 网站设计广州网页设计与制作代码成品
  • 网站 app开发 财务做帐网站如何在百度刷排名
  • dw网页制作破解版关于seo的行业岗位有哪些
  • 代做毕业设计网站哈尔滨百度网络推广
  • 网页设计学徒培训招生杭州seo网
  • 小装修网站开发费用网站网络营销
  • 长沙做网站设计宁宁网seo
  • 成都旅游景点大全排名seo技术培训唐山
  • 阿里云做网站买什么如何创建自己的域名
  • 做网站最烂公司短期培训学什么好
  • 手机做网站怎么做怎么做起泡胶
  • 哪些公司网站做的好公司以优化为理由裁员合法吗
  • 网站建设哪家好胆中毒百度信息流广告推广
  • 网络促销分类 网站促销qq空间刷赞网站推广
  • 如何建设内网网站看片子用什么app免费苹果手机
  • 靠谱网站建设公司收费广州优化网站排名
  • 人才网站 建设好 窗口域名历史查询工具
  • 网站制作的设计思路seo原创工具
  • 江苏省建设厅网站怎么登不上营销课程培训都有哪些
  • 做网站的经验和体会百度游戏官网
  • 公司网站怎么能被百度收录嘉兴seo外包公司费用
  • 网络工程师app谷歌seo优化中文章
  • 瓦房店 网站建设营销软件代理推广
  • wap企业网站口碑营销成功案例
  • 做app网站的软件叫什么名字吗快排seo软件