专栏首页FunTester“双花”BUG的测试分享

“双花”BUG的测试分享

“双花”一词我是从区块链领域的听到的,查了一下资料,基本所有的引用都是基于区块链,但是今天所讲的“双花”不是区块链领域,而是普通的接口测试中遇到的BUG,由于概念一致,所以采用“双花”一词。双花,顾名思义,花了两次,一分钱或者交换流通的物品。下面分享一下自己在工作中遇到的一个双花的BUG的测试方案和原因解释。

场景:有一个兑换活动,大概金币兑换礼物,金币是整个平台流通的货币,礼物价格不等。用户登录活动页后,选择不同的礼物输入数量,点击兑换。接口:活动接口两个:一、获取活动详情以及礼物详情;二、兑换一定数量礼物。兑换记录和消费记录以及个人物品都是老接口,不再赘述。测试工具:Java(不唯一),把接口提供的功能封装为方法,然后通过多线程调用封装号的方法,完成多线程请求兑换接口。

解决方案:在常规测试场景以外,利用多线程并发去测试双花BUG。主要利用了写好的性能测试框架去并发去发送某一个httprequestbase对象,通过构造对应的测试数据,检查测试完成后的测试数据,对比发现是否存在双花的BUG。

在兑换接口中,业务逻辑如下:获取用户余额,判断是否足以支付礼品总价,(大于等于时),发起扣币以及记录相关封装模块功能。

用户A,设置用户余额100,000,兑换价值100的礼物,并发1,010次。最终结果,用户余额为零,兑换的1,000个改礼物,各种记录正常。最后10次响应结果为用户余额不足。

BUG描述:在完成测试时,用户获取到的礼物数量大于1000,余额为零。最后10次请求,有一些是响应成功的。

BUG复盘,在获取完用户余额和判断完总价之后,发起扣费等业务时,并没有重新校验用户余额(或者说改过程是非原子操作不安全),这样导致了最后扣费的时候,使用的用户余额是旧的数值,其他线程也尚未完成扣费,造成了用户的一份金币,被当做两份金币消费了,也就是双花。

下面是测试代码,主要用到了自己写的测试框架,把HttpRequestBase对象组装好之后丢到trhead对象里面,设置请求次数和线程数。

package com.fission.najm.activity.before.workPractise;

import com.fission.najm.base.NajmBase;
import com.fun.frame.excute.Concurrent;
import com.fun.frame.thead.RequestThread;
import net.sf.json.JSONObject;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;

public class Exchange extends NajmBase {
	public String loginKey;
	public String exchangeCode = "";
	public int balance;
	public int coin;
	public HttpRequestBase Rechargerequest;
	public HttpRequestBase exchangeRequest;


	public static void main(String[] args) {
		NajmBase base = new NajmBase();
		Exchange exchange = new Exchange(base);
		exchange.recharge();
		RequestThread requestThread = new RequestThread(exchangeRequest, 101);
		new Concurrent(requestThread,10).start();
		allOver();
	}

	/**
	 * 充值
	 *
	 * @return
	 */
	public JSONObject recharge() {
		JSONObject response = null;
		String url = "http://www.7najm.com/cash/exchangecrecharge";
		JSONObject params = new JSONObject();
		params.put("loginKey", loginKey);
		params.put("exchangeCode", exchangeCode);
		params.put("requestType", "We");
		Rechargerequest = getHttpPost(url, params);
		//response = getHttpResponseEntityByJson(Rechargerequest);
		//output(response);
		return response;
	}

	/**
	 * 获取充值记录
	 *
	 * @return
	 */
	public JSONObject getRechargeRecord() {
		JSONObject response = null;
		String url = "http://www.7najm.com/cash/getecrrecord";
		JSONObject args = new JSONObject();
		args.put("loginKey", loginKey);
		args.put("page", 1);
		args.put("pageSize", 10);
		args.put("requestType", "Web");
		HttpGet httpGet = getHttpGet(url, args);
		response = getHttpResponseEntityByJson(httpGet);
		output(response);
		return response;
	}

	/**
	 * 获取渠道商余额
	 *
	 * @return
	 */
	public JSONObject getBalance() {
		JSONObject response = null;
		String url = "http://www.7najm.com/cash/exchangebalance";
		JSONObject args = new JSONObject();
		args.put("loginKey", loginKey);
		args.put("requestType", "Web");
		HttpGet httpGet = getHttpGet(url, args);
		response = getHttpResponseEntityByJson(httpGet);
		if (response.containsKey("dataInfo"))
			balance = response.getInt("dataInfo");
		output(response);
		return response;
	}

	/**
	 * 获取充值码
	 *
	 * @return
	 */
	public JSONObject getRechargeCode() {
		JSONObject response = null;
		String url = "http://www.7najm.com/cash/getexchangecode";
		JSONObject params = new JSONObject();
		params.put("loginKey", loginKey);
		params.put("requestType", "0");
		params.put("balance", coin);
		exchangeRequest = getHttpPost(url, params);
		response = getHttpResponseEntityByJson(exchangeRequest);
		if (response.containsKey("dataInfo")) {
			exchangeCode = response.getJSONObject("dataInfo").getString("exchangeCode");
		}
		output(response);
		return response;
	}

	/**
	 * 获取充值码列表
	 *
	 * @return
	 */
	public JSONObject getCodeRecord() {
		JSONObject response = null;
		String url = "http://www.7najm.com/cash/getecrecord";
		JSONObject args = new JSONObject();
		args.put("loginKey", loginKey);
		args.put("page", 1);
		args.put("pageSize", 20);
		args.put("requestType", "Web");
		args.put("codeType", 1);
		HttpGet httpGet = getHttpGet(url, args);
		response = getHttpResponseEntityByJson(httpGet);
		output(response);
		return response;
	}
}

往期文章精选

  1. java一行代码打印心形
  2. Linux性能监控软件netdata中文汉化版
  3. 接口测试代码覆盖率(jacoco)方案分享
  4. 性能测试框架
  5. 如何在Linux命令行界面愉快进行性能测试
  6. 图解HTTP脑图
  7. 写给所有人的编程思维
  8. 测试之JVM命令脑图
  9. 将json数据格式化输出到控制台

公众号地图 ☢️ 一起来~FunTester

本文分享自微信公众号 - FunTester(NuclearTester)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 传谷歌即将推出云计算区块链产品

    谷歌的一名人士表示,公司内部的多支团队正在研究区块链技术。区块链技术具有不可篡改的特性,是比特币、以太坊和其他加密货币的基础。不过,这名人士拒绝透露具体细节。

    CloudBest
  • 区块链真能成供应链金融的百世良医?

    消费金融草长莺飞的时代结束,随着互联网企业下沉,供应链搭建需求上升,供应链金融的黄金时代正在到来。

    用户1310347
  • 区块链技术:无比锋利的互联网金融“手术刀”

    区块链技术无疑是当下中国市场最火爆的新技术,尽管它的母体——数字货币遭遇了政策层面的监管,但是有关区块链技术本身的优势让人们看到了它对于传统行业,特别是互联网行...

    用户1310347
  • 【Rust日报】 2019-07-30:CPP工程师的Rust迁移之路

    知乎网友@黄珏珅开设了本专栏,主要针对对 Rust 感兴趣的 C++ 工程师,介绍了完成相同任务 C++ 和 Rust 中的异同,感兴趣可以专注专栏,目前已经有...

    MikeLoveRust
  • solidity智能合约中tx.origin的正确使用场景

    tx.origin是Solidity的一个全局变量,它遍历整个调用栈并返回最初发送调用(或事务)的帐户的地址。在智能合约中使用此变量进行身份验证会使合约容易受到...

    用户1161110
  • solidity智能合约如何判断地址为0或空

    原文链接:https://www.choupangxia.com/2019/07/16/solidity智能合约如何判断地址为0或空/

    用户1161110
  • AI在FinTech金融科技领域最新现状及趋势分析

    自从2016年AlphaGo横空出世后,AI人工智能成为业界的热门话题, 并且在风投领域, FinTech甚至各个传统行业都掀起一场革新浪潮。本文希望简单研究下...

    用户1594945
  • Web3与智能合约交互实战

    在最初学习以太坊的时候,很多人都是自己创建以太坊节点后,使用geth与之交互。这种使用命令行交互的方法虽然让很多程序员感到兴奋(黑客帝国的既视感),但不可能指望...

    Tiny熊
  • 认识区块链,认知区块链——热闹是它们的,我什么也没有

    (请不要将币混淆成区块链)相比去年的火热,今年确实冷清了不少,很多团队发展困难,甚至解散,区块链技术的应用依旧需要时间的催化。

    歪脖贰点零

扫码关注云+社区

领取腾讯云代金券