【AgileTC】_测试用例页面需求对应接口三层结构开发

【AgileTC】_测试用例页面需求对应接口三层结构开发

1.创建controller方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 获取任务列表
*/
@RequestMapping(value = "/listAll")
public Response<?> listAllRecord(@RequestParam(value = "title", required = false) String title,
@RequestParam(value = "owner", required = false) String owner,
@RequestParam(value = "reqId", required = false) String reqId,
@RequestParam(value = "executors", required = false) String executors,
@RequestParam(value = "beginTime", required = false) String beginTime,
@RequestParam(value = "endTime", required = false) String endTime,
@RequestParam(value = "timeLimit",required = false) String timeLimit,
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(value = "lineId") @NotNull(message = "业务线为空") Integer lineId,
@RequestAttribute Integer channel) {
return Response.success(recordService.getAllRecord(
new RecordAllQueryReq(title,owner,reqId,executors,beginTime,endTime,timeLimit,pageNum,pageSize,lineId,channel)));
}
  • 注意@RequestParam注解与@RequestAttribute注解的区别,前者是从前端传递过来的请求体参数中获取数据,后者是从请求域中获取数据。要注意请求体和请求域是两个完全不同的概念。

2.根据需要的参数编写请求体对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Data
public class RecordAllQueryReq {

private String title;

private String owner;

private String reqId;

private String executors;

private String beginTime;

private String timeLimit;

private String endTime;

private Integer pageNum;

private Integer pageSize;

private Integer lineId;

private Integer channel;

public RecordAllQueryReq(String title, String owner, String reqId, String executors, String beginTime, String endTime, String timeLimit, Integer pageNum, Integer pageSize,Integer lineId,Integer channel) {
this.title = title;
this.owner = owner;
this.reqId = reqId;
this.executors = executors;
this.beginTime = beginTime;
this.endTime = endTime;
this.timeLimit = timeLimit;
this.pageNum = pageNum;
this.pageSize = pageSize;
this.lineId = lineId;
this.channel = channel;
}
}

3.根据需要返回的参数编写响应体对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Data
public class OeAllRecordListResp {

private Long recordId;

private Long caseId;

private String title;

private String requirementId;

private String owner;

private String ownerCn;

private String executor;

private String executorCn;

private Integer successNum;

private Integer blockNum;

private Integer bugNum;

private Integer executeNum;

private Integer totalNum;

private Date createTime;

/**
* 圈选用例内容
*/
private String chooseContent;

/**
* 计划周期-开始时间
*/
private Date expectStartTime;

/**
* 计划周期-结束时间
*/
private Date expectEndTime;

/**
* 用例描述
*/
private String description;

private String tags;
}

4.编写service接口中的抽象方法

1
2
3
4
/**
* 根据条件查询所有执行任务
*/
PageResult<OeAllRecordListResp> getAllRecord(RecordAllQueryReq req);
  • 如果请求数据或者响应数据较多,一般定义两个javabean类来装载请求体参数和响应体参数

5.编写serviceImpl方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
@Override
public PageResult<OeAllRecordListResp> getAllRecord(RecordAllQueryReq req) {
PageResult<OeAllRecordListResp> res = new PageResult<>();

Date beginTime = transferTime(req.getBeginTime());
Date endTime = transferTime(req.getEndTime());

String timeLimit = req.getTimeLimit();
if (!StringUtils.isEmpty(timeLimit)) {
endTime = new Date();
if (timeLimit.equals(ONEDAY)) {
beginTime = getLastTime(-1);
}else if (timeLimit.equals(ONEWEEK)) {
beginTime = getLastTime(-7);
}
}

//分页
PageHelper.startPage(req.getPageNum(), req.getPageSize());

List<ExecRecord> recordList = recordMapper.getAllRecord(
req.getTitle(),req.getOwner(),req.getExecutors(),beginTime,endTime,req.getLineId(),req.getChannel()
);

if (!CollectionUtils.isEmpty(recordList)) {
List<OeAllRecordListResp> list = new ArrayList<>();
for (ExecRecord execRecord : recordList) {
OeAllRecordListResp resp = new OeAllRecordListResp();

//基础信息传入
resp.setRecordId(execRecord.getId());
resp.setCaseId(execRecord.getCaseId());
resp.setTitle(execRecord.getTitle());
resp.setOwner(execRecord.getOwner());
resp.setOwnerCn(execRecord.getOwnerCn());
resp.setExecutor(execRecord.getExecutors());
resp.setExecutorCn(execRecord.getExecutorsCn());
resp.setCreateTime(execRecord.getGmtCreated());
resp.setTags(execRecord.getTags());
resp.setChooseContent(execRecord.getChooseContent());
resp.setDescription(execRecord.getDescription());
resp.setExpectStartTime(
compareToOriginalDate(execRecord.getExpectStartTime()) ? null : execRecord.getExpectStartTime());
resp.setExpectEndTime(
compareToOriginalDate(execRecord.getExpectEndTime()) ? null : execRecord.getExpectEndTime());

//每个测试用例对应一个需求id
TestCase testCase = caseMapper.selectByPrimaryKey(execRecord.getCaseId());
resp.setRequirementId(testCase.getRequirementId());

//统计数据其实本质上不能通过数据库去获取,因为还需要考虑chooseContent
JSONObject object = getData(new MergeCaseDto(execRecord.getCaseId(), execRecord.getChooseContent(), execRecord.getCaseContent(), execRecord.getEnv()));

//统计信息传入
resp.setSuccessNum(object.getInteger("successCount"));
resp.setBlockNum(object.getInteger("blockCount"));
resp.setBugNum(object.getInteger("failCount"));
resp.setTotalNum(object.getInteger("totalCount"));
resp.setExecuteNum(object.getInteger("passCount"));

list.add(resp);
}

res = PageResult.buildPage(list, ((Page<ExecRecord>) recordList).getTotal());
}

return res;
}

/**
* ☆将当前record的操作记录和用例集的内容进行merge,返回合并后的内容
*/
public JSONObject getData(MergeCaseDto dto) {
String caseContent = caseMapper.selectByPrimaryKey(dto.getCaseId()).getCaseContent();
JSONObject content = JSON.parseObject(caseContent);
// oe测有圈选条件
if (!StringUtils.isEmpty(dto.getChooseContent()) && !dto.getChooseContent().contains(OE_PICK_ALL)) {
PickCaseDto pickCaseDto = JSON.parseObject(dto.getChooseContent(), PickCaseDto.class);

// 似乎是想用BFS做广度遍历
JSONObject caseRoot = content.getJSONObject("root");
Stack<JSONObject> objCheck = new Stack<>();
Stack<IntCount> iCheck = new Stack<>();
objCheck.push(caseRoot);

//获取对应级别用例
if (!CollectionUtils.isEmpty(pickCaseDto.getPriority())) {
TreeUtil.getPriority(objCheck, iCheck, caseRoot, pickCaseDto.getPriority());
}
if (!CollectionUtils.isEmpty(pickCaseDto.getResource())) {
TreeUtil.getChosenCase(caseRoot, new HashSet<>(pickCaseDto.getResource()), "resource");
}
} else {
// done侧有环境选择
if (EnvEnum.TestQaEnv.getValue().equals(dto.getEnv()) || EnvEnum.TestRdEnv.getValue().equals(dto.getEnv())) {
// 似乎是想用BFS做广度遍历
JSONObject caseRoot = content.getJSONObject("root");
Stack<JSONObject> objCheck = new Stack<>();
Stack<IntCount> iCheck = new Stack<>();
objCheck.push(caseRoot);

// 这里就是默认圈选全部用例
TreeUtil.getPriority0(objCheck, iCheck, caseRoot);
}
}
//合并用例
String recordContent = dto.getRecordContent();
JSONObject recordObj = new JSONObject();

if (StringUtils.isEmpty(recordContent)) {
// 脏数据,不管
} else if (recordContent.startsWith("[{")) {
for (Object o : JSON.parseArray(recordContent)) {
recordObj.put(((JSONObject) o).getString("id"), ((JSONObject) o).getLong("progress"));
}
} else {
recordObj = JSON.parseObject(recordContent);
}

IntCount execCount = new IntCount(recordObj.size());
TreeUtil.mergeExecRecord(content.getJSONObject("root"), recordObj, execCount);
return TreeUtil.parse(content.toJSONString());
}

private Date transferTime(String time) {
if (time == null) {
return null;
}
return transferStrToDateInSecond(time);
}

/**
* 获取几天前的时间
* 输入为负数表示几天前,为正数表示几天后。
*/
private Date getLastTime(int index) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH,index);
return calendar.getTime();
}
  • 此处使用了分页查询:
    1. 声明返回值为分页对象PageResult<OeAllRecordListResp>
    2. 为分页助手输入页码和每页个数参数PageHelper.startPage(req.getPageNum(), req.getPageSize())
    3. 输入查询结果数组来创建分页对象PageResult.buildPage(list, ((Page<ExecRecord>) recordList).getTotal())
  • 此处注意不能直接获取数据库中的测试任务执行结果,要用getData方法来获取。
  • 此处使用了Calendar类来帮助获取几天前的时间。

6.编写mapper接口中的抽象方法

1
2
3
4
5
6
7
List<ExecRecord> getAllRecord(@Param("title") String title,
@Param("owner") String owner,
@Param("executors") String executors,
@Param("beginTime") Date beginTime,
@Param("endTime") Date endTime,
@Param("lineId") Integer lineId,
@Param("channel") Integer channel);
  • mapper接口的抽象方法中定义sql语句的输入参数。

7.在mapper配置文件中编写sql语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<select id="getAllRecord" resultMap="ResultMapWithBLOBs">
select
r.*
from exec_record r
left join test_case ca on r.case_id = ca.id
where r.is_delete = 0 and ca.channel = #{channel,jdbcType=INTEGER} and ca.product_line_id = #{lineId,jdbcType=INTEGER}
<if test="title != null and title != ''">
and r.title like CONCAT('%',#{title,jdbcType=VARCHAR},'%')
</if>
<if test="owner != null and owner != ''">
and r.owner = #{owner,jdbcType=VARCHAR}
</if>
<if test="executors != null and executors != ''">
and r.executors = #{executors,jdbcType=VARCHAR}
</if>
<if test="beginTime != null">
and r.gmt_created &gt;= #{beginTime,jdbcType=TIMESTAMP}
</if>
<if test="endTime != null">
and r.gmt_created &lt;= #{endTime,jdbcType=TIMESTAMP}
</if>
order by r.id desc
</select>
  • 这里其实同时使用了内连接和外连接:left join后是外连接的表,on后是外连接的条件,这是一种显式外连接;where后跟的是内连接的条件,这是一种隐式内连接。
  • 这里可以发现mapper方法可以直接传入Date类型的java数据与数据库中TIMESTAMP类型数据进行比较。
  • 注意left join的查询结果形式,如果left join两边的两张表是一对多或者多对多关系,那么左边的基表中的同一行数据完全有可能在查询结果中显示多次,对应上右边连接表中的不同行。