准备工作数据库校验用户准备工作

Posted wsfj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了准备工作数据库校验用户准备工作相关的知识,希望对你有一定的参考价值。

准备工作

1、添加依赖

    <!--redis依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--fastjson依赖-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.33</version>
    </dependency>
    <!--jwt依赖-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
    </dependency>

2、添加Redis相关配置

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
 
/**
 * Redis使用FastJson序列化
 * 
 * @author sg
 */
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>

 
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
 
    private Class<T> clazz;
 
    static
    
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    
 
    public FastJsonRedisSerializer(Class<T> clazz)
    
        super();
        this.clazz = clazz;
    
 
    @Override
    public byte[] serialize(T t) throws SerializationException
    
        if (t == null)
        
            return new byte[0];
        
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    
 
    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    
        if (bytes == null || bytes.length <= 0)
        
            return null;
        
        String str = new String(bytes, DEFAULT_CHARSET);
 
        return JSON.parseObject(str, clazz);
    
 
 
    protected JavaType getJavaType(Class<?> clazz)
    
        return TypeFactory.defaultInstance().constructType(clazz);
    
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
 
@Configuration
public class RedisConfig 
 
    @Bean
    @SuppressWarnings(value =  "unchecked", "rawtypes" )
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
 
        FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);
 
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
 
        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
 
        template.afterPropertiesSet();
        return template;
    

3、响应类

import com.fasterxml.jackson.annotation.JsonInclude;
 
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> 
    /**
     * 状态码
     */
    private Integer code;
    /**
     * 提示信息,如果有错误时,前端可以获取该字段进行提示
     */
    private String msg;
    /**
     * 查询到的结果数据,
     */
    private T data;
 
    public ResponseResult(Integer code, String msg) 
        this.code = code;
        this.msg = msg;
    
 
    public ResponseResult(Integer code, T data) 
        this.code = code;
        this.data = data;
    
 
    public Integer getCode() 
        return code;
    
 
    public void setCode(Integer code) 
        this.code = code;
    
 
    public String getMsg() 
        return msg;
    
 
    public void setMsg(String msg) 
        this.msg = msg;
    
 
    public T getData() 
        return data;
    
 
    public void setData(T data) 
        this.data = data;
    
 
    public ResponseResult(Integer code, String msg, T data) 
        this.code = code;
        this.msg = msg;
        this.data = data;
    

4、工具类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
 
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
 
/**
 * JWT工具类
 */
public class JwtUtil 
 
    //有效期为
    public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一个小时
    //设置秘钥明文
    public static final String JWT_KEY = "sangeng";
 
    public static String getUUID()
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        return token;
    
    
    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @return
     */
    public static String createJWT(String subject) 
        JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
        return builder.compact();
    
 
    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @param ttlMillis token超时时间
     * @return
     */
    public static String createJWT(String subject, Long ttlMillis) 
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
        return builder.compact();
    
 
    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) 
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null)
            ttlMillis=JwtUtil.JWT_TTL;
        
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        return Jwts.builder()
                .setId(uuid)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("sg")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);
    
 
    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) 
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
        return builder.compact();
    
 
    public static void main(String[] args) throws Exception 
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";
        Claims claims = parseJWT(token);
        System.out.println(claims);
    
 
    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() 
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    
    
    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception 
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    
 
 
import java.util.*;
import java.util.concurrent.TimeUnit;
 
@SuppressWarnings(value =  "unchecked", "rawtypes" )
@Component
public class RedisCache

    @Autowired
    public RedisTemplate redisTemplate;
 
    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    
        redisTemplate.opsForValue().set(key, value);
    
 
    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    
 
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    
        return expire(key, timeout, TimeUnit.SECONDS);
    
 
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    
        return redisTemplate.expire(key, timeout, unit);
    
 
    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    
 
    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    
        return redisTemplate.delete(key);
    
 
    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection)
    
        return redisTemplate.delete(collection);
    
 
    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    
 
    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    
        return redisTemplate.opsForList().range(key, 0, -1);
    
 
    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        
            setOperation.add(it.next());
        
        return setOperation;
    
 
    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    
        return redisTemplate.opsForSet().members(key);
    
 
    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    
        if (dataMap != null) 
            redisTemplate.opsForHash().putAll(key, dataMap);
        
    
 
    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    
        return redisTemplate.opsForHash().entries(key);
    
 
    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    
        redisTemplate.opsForHash().put(key, hKey, value);
    
 
    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    
 
    /**
     * 删除Hash中的数据
     * 
     * @param key
     * @param hkey
     */
    public void delCacheMapValue(final String key, final String hkey)
    
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hkey);
    
 
    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    
 
    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    
        return redisTemplate.keys(pattern);
    
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
 
public class WebUtils

    /**
     * 将字符串渲染到客户端
     * 
     * @param response 渲染对象
     * @param string 待渲染的字符串
     * @return null
     */
    public static String renderString(HttpServletResponse response, String string) 
        try
        
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
        
        catch (IOException e)
        
            e.printStackTrace();
        
        return null;
    

5、实体类

import java.io.Serializable;
import java.util.Date;
 
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable 
    private static final long serialVersionUID = -40356785423868312L;
    
    /**
    * 主键
    */
    private Long id;
    /**
    * 用户名
    */
    private String userName;
    /**
    * 昵称
    */
    private String nickName;
    /**
    * 密码
    */
    private String password;
    /**
    * 账号状态(0正常 1停用)
    */
    private String status;
    /**
    * 邮箱
    */
    private String email;
    /**
    * 手机号
    */
    private String phonenumber;
    /**
    * 用户性别(0男,1女,2未知)
    */
    private String sex;
    /**
    * 头像
    */
    private String avatar;
    /**
    * 用户类型(0管理员,1普通用户)
    */
    private String userType;
    /**
    * 创建人的用户id
    */
    private Long createBy;
    /**
    * 创建时间
    */
    private Date createTime;
    /**
    * 更新人
    */
    private Long updateBy;
    /**
    * 更新时间
    */
    private Date updateTime;
    /**
    * 删除标志(0代表未删除,1代表已删除)
    */
    private Integer delFlag;

数据库校验用户准备工作

从之前的分析我们可以知道,我们可以自定一个UserDetailsService,让SpringSecurity使用我们的UserDetailsService。我们自己的UserDetailsService可以从数据库中查询用户名和密码。

准备工作

我们先创建一个用户表,建表语句如下:

CREATE TABLE `sys_user` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT \'主键\',
  `user_name` VARCHAR(64) NOT NULL DEFAULT \'NULL\' COMMENT \'用户名\',
  `nick_name` VARCHAR(64) NOT NULL DEFAULT \'NULL\' COMMENT \'昵称\',
  `password` VARCHAR(64) NOT NULL DEFAULT \'NULL\' COMMENT \'密码\',
  `status` CHAR(1) DEFAULT \'0\' COMMENT \'账号状态(0正常 1停用)\',
  `email` VARCHAR(64) DEFAULT NULL COMMENT \'邮箱\',
  `phonenumber` VARCHAR(32) DEFAULT NULL COMMENT \'手机号\',
  `sex` CHAR(1) DEFAULT NULL COMMENT \'用户性别(0男,1女,2未知)\',
  `avatar` VARCHAR(128) DEFAULT NULL COMMENT \'头像\',
  `user_type` CHAR(1) NOT NULL DEFAULT \'1\' COMMENT \'用户类型(0管理员,1普通用户)\',
  `create_by` BIGINT(20) DEFAULT NULL COMMENT \'创建人的用户id\',
  `create_time` DATETIME DEFAULT NULL COMMENT \'创建时间\',
  `update_by` BIGINT(20) DEFAULT NULL COMMENT \'更新人\',
  `update_time` DATETIME DEFAULT NULL COMMENT \'更新时间\',
  `del_flag` INT(11) DEFAULT \'0\' COMMENT \'删除标志(0代表未删除,1代表已删除)\',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT=\'用户表\'

引入MybatisPuls和mysql驱动的依赖

      <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

配置数据库信息

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/sg_security?characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

定义Mapper接口

public interface UserMapper extends BaseMapper<User> 

修改User实体类

类名上加@TableName(value = "sys_user") ,id字段上加 @TableId

配置Mapper扫描

@SpringBootApplication
@MapperScan("com.sangeng.mapper")
public class SimpleSecurityApplication 
    public static void main(String[] args) 
        ConfigurableApplicationContext run = SpringApplication.run(SimpleSecurityApplication.class);
        System.out.println(run);
    

添加junit依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>    

测试MP是否能正常使用

@SpringBootTest
public class MapperTest 
 
    @Autowired
    private UserMapper userMapper;
 
    @Test
    public void testUserMapper()
        List<User> users = userMapper.selectList(null);
        System.out.println(users);
    

接口自动化测试流程和相关准备工作

第一步: 拿到需求文档、UI交互图(原型图)、数据库表设计文档、接口文档

1问:为什么要拿到这些文档资料呢?

1答:

①.《需求文档》,明确定义了:各个表单字段的限制条件;相关场景逻辑校验;

②.《UI交互图》,明确定义了:各单页面需展示的数据;页面之间的交互;

③.《数据表设计文档》,结合UI图和需求文档,明确定义了:表字段规则、表N多N关系(一对一、一对多、多对多);

④.《接口文档》,结合需求文档和UI和数据表,明确定义了:接口名,各个入参值,各个返回值,和其他相关信息;

2细节:

①.大多数公司,针对这四类文档资料都整理的不规范,或者没能及时更新;

②.这会导致接口测试用例的编写没有一个绝对可靠的需求来源

③.因为,接口测试用例本质是针对各个表字段的校验;

④.所以需求文档里对各个表单字段的限制条件,产品人员务必都要写清楚,不要遗漏限制条件;

⑤.而接口文档里针对各个用例场景的返回值,文档里务必都要及时填写和更新;

⑥.针对接口文档返回值的字段:比如code=0表示登录成功,code=1表示密码错误;code=2表示无网络;code=3表示账号错误,等等类似code值有不同含义;

3注意:

①.所以,在写接口测试用例之前,务必要先核对需求文档和接口文档,以最正确的需求文档和接口文档,来编写接口用例,才能得到最正确的结果;

②.先实现单接口解耦;后续按照业务场景组合多个接口;

 

 

第二步: 拿到个人编写的接口用例模板,针对特定单接口编写接口用例

 

1重点:要及时更新接口用例模板.xlsx,保证用例模板的准确性;

2假设有一个后台系统banner广告位子模块,以特定单接口-createBanner-新增banner,来讲解编写单接口的接口用例的操作流程:

1)新建一个xlsx后缀的excel文档,excel名改为:banner.xlsx

2)打开banner.xlsx,第一个sheet页名称改为:创建banner

3)打开接口用例模板.xlsx,把名称为【接口测试demo】的sheet页里的所有内容复制到banner.xlsx的名称为【创建banner】的空sheet页;

4)在名称为【创建banner】的sheet页里,做如下步骤的操作:

4-1)针对第一行各单元格数据(第一行各单元格是用来填写相关参数名和用于标识的参数名):

按照实际需求来灵活配置:操作入参名所在的单元格;操作返回参数名所在的单元格;

入参名都用红色字体表示;返回参数名都用绿色字体表示;

比如:修改某个入参名,删除多余的一个入参名所在的一个列,新增一个列来填写一个新的入参名;

比如:修改某个返回参数名,删除多余的一个返回参数名所在的一个列,新增一个列来填写一个新的返回参数名;

②跟入参名跟返回参数名所在的单元格无关的单元格,都保持原位置和原单元格名称即可。

当然也支持任意移动位置和更改单元格名,但不建议更改单元格名因为更改了单元格名后代码也要跟着变动)

 

4-2)针对第二行各单元格数据(第二行各单元格是用来填写默认值):

①单元格【验证字段】的值:不填,为空;

②单元格【接口地址】的默认值值是接口相对路径,结合实际情况比如更改为:/api/b.banner/creBanner,用于拼接该接口请求的绝对路径;

③单元格【请求方式】的默认值值是请求方式,结合实际情况比如更改为:post;

④单元格【编号】的值:不填,为空;

⑤单元格【返回值和断言结果的所在行下标】的值:不填,为空;

⑥单元格【用例类型】的值:不填,为空;

⑦单元格【用例目的】的值:不填,为空;

⑧单元格【用例名】的值:不填,为空;

⑨单元格【返回值】的值:不填,为空;

⑩单元格【断言结果】的值:不填,为空;

细节1:如果入参名所在的单元格和返回参数名所在的单元格,单元格值可以重复且出现频率高,那就可以填写默认值

细节2:如果入参名所在的单元格和返回参数名所在的单元格,单元格值不可以重复只能是唯一值,那就单元格值为空

 

4-3)针对第三行及大于第三行的各单元格数据(用于填写具体值):

结合接口用例模板.xlsx的名为【接口测试用例模板】sheet页称为A,在banner.xlsx的名为【创建banner】sheet页称为B,进行灵活填写;

操作步骤:

①针对某个接口字段,复制A的三列数据【用例类型】【用例目的】【示范的用例名】,复制给B的三列数据【用例类型】【用例目的】【用例名】;

②然后删除不需要的用例名对应的行数据;

③更改各条剩下的用例名,比如更改字段名和相关数字等数据;(基本99%都要替换掉从模板复制过来的数据);

④B的这列数据【接口地址】,务必都填写DF;

⑤B的这列数据【请求方式】,务必都填写DF;

⑥B的这列数据【编号】,务必都填写文本形式的数字且数字递增,值从01开始(文本形式的数字可以再excel里设置);

⑦B的这列数据【返回值和断言结果的所在行下标】,务必都填写数字且数字递增,值从2开始;

⑧B的这列数据【验证字段】,灵活按照实际入参名来填写;

⑨B的两列数据【code】【data】都务必结合接口文档和需求文档,填写正确的值;(若哪些值觉得不合理,后期可以让产品或开发人员进行修改);

⑩B的两列数据【code】【msg】都务必结合接口文档和需求文档,填写正确的值;(若哪些值觉得不合理,后期可以让产品或开发人员进行修改);

细节1:DF表示默认值,取值于第二行各自的单元格值(在脚本里有做相关转化和校验);

细节2:&$,表示一个变量,由脚本内部赋值。比如: variable = "$count": 666 ;

细节3:B的两列数据【返回值】【断言结果】都默认不填写,这两列数据都是由相关脚本返回的值;

细节4: 相关颜色的标注,可结合实际来灵活填写;

细节5: 列数据【data】和列数据【msg】不可能同时有值;

细节6:待补充列表字段【预期值】,把【code】+【data】&【code】+【msg】这样组合为一个dict,回传写入【banner(包含接口返回值和断言结果).xlsx】;

 

 

第三步: 结合项目框架,做相关流程的脚本操作

1)第一,执行必须操作的步骤(按项目二级目录的排序顺序来执行)

1. 在二级模块名【/data】内:编写脚本d_add_banner.py; (在子类D_add_banner的父类属性variable可结合banner.xlsx内的接口用例赋值情况,来重写该variable属性值;)

2. 在二级模块名【/excel】内:存放banner.xlsx;

3.在二级模块名【/expectedResult】内:编写脚本e_add_banner ; (第一次编写子类E_add_banner时,直接继承且不重写任何一个父类方法;调试期间,可按照实际重写父类相关方法;);

4. 在二级模块名【/model】内:编写脚本m_add_banner.py; (第一次编写子类M_add_banner.py时,直接继承且不重写任何一个父类方法;调试期间,可按照实际重写父类相关方法;)

5. 在二级模块名【/optimize】内:编写脚本o_add_banner.py; (第一次编写子类O_add_banner.py时,直接继承且不重写父类的sleep方法;调试期间,可按照实际重写父类的sleep方法;)

6. 在二级模块名【/validate】内:编写脚本v_add_banner.py;(第一次编写子类V_add_banner.py时,直接继承且不重写父类的compareResult方法;调试期间,可按照实际重写父类的compareResult方法;)

7. 在二级模块名【/writeCellValue】内:编写脚本w_add_banner.py;

 

细节:各步骤对应的类都相对解耦,数据源基本都来自同个上游接口--d_add_banner.py内的子类D_add_banner所调用的父类方法excel_data()的返回值;

 

2)第二,再执行非必须操作的步骤(按项目二级目录的排序顺序来执行)

1. 在二级模块名【/fileAttribute】内:编写脚本f_add_banner.py;

2. 在二级模块名【/public】内:存放图片视频等相关测试数据;

 

3)第三,针对单接口createBanner,调试接口请求

1. 在二级模块名【/controller】内:编写脚本c_add_banner.py; (第一次编写C_add_banner.py时,务必对某个特定的父类方法进行重写,比如:父类方法add,父类方法update;)

细节1: 相关入参值都采取参数化;

细节2:在【if __name__ == ‘__main__‘:】下方区域,进行单元测试的调试;

细节3:用于调试的数据源data,可以在【d_add_banner.py内的子类D_add_banner所调用的父类方法excel_data()的返回值】这边获取,获取符合要求的其中一条数据来当做数据源;

 

4)第四,针对单接口createBanner,结合ddt,遍历执行所有的接口测试用例

1. 在二级模块名【/testcase】内:编写脚本test_001_add_banner_testcase.py;

细节1:可直接复制其余现成的脚本,用ctrl+R快捷键统一替换相关关键字;

细节2:针对不同接口,脚本需增加/减少特定的代码;(比如【修改】接口可能比【新增接口】需要多调用sleep方法,防止程序执行过快导致接口请求异常返回报错信息)

细节3:成功执行该脚本后,会在二级模块名【/excel】里生成对应的【banner(包含接口返回值和断言结果).xlsx】,会包含每条接口用例的返回值和断言结果;

 

 

5)第五,核对生成的数据

1. 查看【banner(包含接口返回值和断言结果).xlsx-【创建banner】sheet页的 返回值和断言结果】,大概看一下返回值的数据是否正确;如果有错误的返回值,则继续排查和优化相关脚本;

 

6)第六,执行单一入口函数

 

步骤:

1.配置根目录run.py相关参数信息;

2.执行run.py

3.生成html格式的测试报告;并,发送相关报告至指定邮箱;

后期拓展:

1.部署线上jenkins服务,部署本地/线上python环境,部署本地/线上wamp环境,CI持续集成;

2.熟悉相关linux命令;优化相关脚本逻辑;

3.部署线上禅道服务,实现主要功能:实时写入bug&获取bug清单&更改bug状态,下载最新包含符合筛选条件的bug的excel文档;

 

 

 python相关核心脚本如下:

 1 # coding:utf-8
 2 ‘‘‘
 3 @file: test_002_update_banner_testcase.py
 4 @author: jingsheng hong
 5 @ide: PyCharm
 6 @createTime: 2019年07月29日  10点21分
 7 @contactInformation: 727803257@qq.com
 8 ‘‘‘
 9 
10 
11 import unittest
12 import ddt
13 from data.b.banner.d_update_banner              import D_update_banner
14 from controller.b.banner.c_update_banner        import C_update_Banner
15 from expectedResult.b.banner.e_update_banner    import E_update_banner
16 from validate.b.banner.v_update_banner          import V_update_banner
17 from optimize.b.banner.o_update_banner          import O_update_banner
18 from writeCellValue.b.banner.w_update_banner    import W_update_banner
19 
20 excel_data = D_update_banner().excel_data()
21 
22 @ddt.ddt
23 class Test_update_banner(unittest.TestCase):
24     ‘‘‘【更新banner】接口的接口测试用例集合‘‘‘
25 
26     def setUp(self):
27         pass
28 
29     def tearDown(self):
30         pass
31 
32     @ddt.data(*excel_data)
33     def test_update_banner(self,data):
34         O_update_banner(data).sleep()
35         O_update_banner(data).printAllParamsLog()
36         expectResult    =  E_update_banner(data).update()
37         actualResult    =  C_update_Banner(data).update()
38         W_update_banner(data).writeReturnValue(actualResult)
39         O_update_banner(data).printLog(expectResult,actualResult)
40         compareResult   =  V_update_banner(expectResult,actualResult).compareResult()
41         assertResult    =  V_update_banner(expectResult,actualResult).assertResult()
42         W_update_banner(data).writeAssertResult(assertResult)
43         self.assertTrue(compareResult)
44 
45 if __name__ =="__main__":
46     unittest.main()

 

 

php相关控制层脚本如下:

  1 <?php
  2 /**
  3  * Created by PhpStorm.
  4  * User: Administrator
  5  * Date: 2019/03/25
  6  * Time: 19:02
  7  */
  8 
  9 namespace app\api\controller\b;
 10 
 11 use app\api\controller\ApiCommon;
 12 use app\api\controller\utils\File;
 13 use app\api\model\ModelBanner;
 14 use app\api\validate\V_Banner;
 15 use think\Db;
 16 use think\Response;
 17 
 18 class Banner  extends ApiCommon
 19 
 20 
 21     /**  后台查询banner列表(可传id获取单个banner信息)
 22      * @return \think\Response
 23      */
 24     public function showBannerA()
 25         $id = input(‘param.id‘);
 26         $page = input(‘param.page‘);
 27         $last = input(‘param.last‘);
 28         //实例化content,查询all
 29         $mBanner = new ModelBanner();
 30         $list = $mBanner->showBannerA($id,$page,$last);
 31         $count = $mBanner->countBanner();
 32         list_upload_image_path_format($list,‘image‘);
 33         if ($list)
 34             $data = [
 35                 ‘code‘=>0,
 36                 ‘count‘=>$count==0?‘‘:$count,
 37                 ‘data‘=>$list,
 38             ];
 39             return response($data,0,array(),‘json‘);
 40         else
 41             return api_list_not_more();
 42         
 43     
 44     //Banner排序接口
 45     public function sortBanner()
 46         //获取到的id和sort值
 47         $param = input(‘param.‘);
 48         if (empty($param))
 49             return api_param_error();
 50         
 51         $all=[];
 52         for ($i = 0;$i<count($param[‘id‘]);$i++)
 53             //验证参数
 54             $validate = new V_Banner();
 55             if (!$validate->scene(‘sort‘)->check([‘sort‘=>$param[‘sort‘][$i]])) 
 56                 return api_param_error($validate->getError());
 57             
 58             $info[‘id‘]=$param[‘id‘][$i];
 59             $info[‘sort‘]=$param[‘sort‘][$i];
 60             $all[] = $info;
 61         
 62         $banner = new ModelBanner();
 63         $res = $banner->saveAll($all);
 64         if ($res)
 65             return api_success(‘操作成功‘);
 66         else
 67             return api_error();
 68         
 69     
 70 
 71     /**  删除banner(传id删除单个banner或传数组批量删除)
 72      * @return \think\Response
 73      */
 74     public function delBanner()
 75         $id = input(‘param.id‘);
 76         $mBanner = new ModelBanner();
 77         $banner =$mBanner->delBanner($id);
 78         if ($banner)
 79             return api_success(‘成功删除‘);
 80         else
 81             return api_error();
 82         
 83     
 84     /**  创建banner
 85      * @return Response
 86      */
 87     public function creBanner()
 88         //获取内容信息    id title img_path status  type
 89         $info = input(‘param.‘);
 90         //验证参数
 91         $validate =  new V_Banner();
 92         if (!$validate->scene(‘create‘)->check($info)) 
 93             return api_param_error($validate->getError());
 94         
 95         if (strpos($info[‘sort‘],‘.‘))
 96             return api_error(‘排序只能是整数‘);
 97         
 98         $banner = new ModelBanner();
 99         $sort = $banner->where(‘sort‘,$info[‘sort‘])->find();
100         if ($sort)
101             return api_error(‘此排序数字已经存在‘);
102         
103         $file = new File();
104         $files = $file->upload(‘Banner‘,‘image‘,true);
105         //判断是否上传文件
106         if ($files instanceof Response) 
107             return api_error(‘没有上传图片‘);
108         
109         //检查是否上传成功
110         if ($files[0][‘is_success‘]) 
111             // 启动事务
112             Db::startTrans();
113             try 
114                 $info[‘image‘] = $files[0][‘file_path‘];
115 
116                 $res = $banner->save($info);
117                 if ($res)
118                     // 提交事务
119                     Db::commit();
120                     return api_success(‘创建成功!‘);
121                 
122              catch (\Exception $e) 
123                 // 回滚事务
124                 Db::rollback();
125                 return api_success(‘创建失败!‘);
126             
127         else
128             return api_error($files[0][‘error_msg‘]);
129         
130 
131     
132     /** 修改banner
133      * @return \think\Response
134      */
135     public function upBanner()
136 
137         //获取内容信息    id title img_path status  type
138         $info = input(‘param.‘);
139         //验证参数
140         $validate =  new V_Banner();
141         if (!$validate->scene(‘update‘)->check(input(‘param.‘))) 
142             return api_param_error($validate->getError());
143         
144         if (strpos($info[‘sort‘],‘.‘))
145             return api_error(‘排序只能是整数‘);
146         
147         $banner = new ModelBanner();
148         $sort = $banner->where(‘sort‘,$info[‘sort‘])->find();
149         if ($sort)
150             if (!$banner->where(‘sort‘,$info[‘sort‘])->find($info[‘id‘]))
151                 return api_error(‘此排序数字已经存在‘);
152             
153         
154         $file = new File();
155         $files = $file->upload(‘Banner‘,‘image‘,true);
156 
157         //判断是否上传文件
158         if (!$files instanceof Response) 
159             //判断是否符合大小和格式
160             if (!$files[0][‘is_success‘]) 
161                 return api_error($files[0][‘error_msg‘]);
162             
163             $info[‘image‘] = $files[0][‘file_path‘];
164         
165         // 启动事务
166         Db::startTrans();
167         try 
168             $mBanner = new ModelBanner();
169             $res = $mBanner->save($info,[‘id‘=>$info[‘id‘]]);
170             if ($res)
171                 // 提交事务
172                 Db::commit();
173                 return api_success(‘修改成功!‘);
174             
175          catch (\Exception $e) 
176             // 回滚事务
177             Db::rollback();
178             return api_error(‘修改失败!‘);
179         
180     
181 
182 
183 
184     /**  banner上下线功能
185      * @return Response
186      */
187     public function togStatus()
188         $id = input(‘param.id‘);
189         if (empty($id))
190             return api_param_error();
191         
192         $mBanner = new ModelBanner();
193         $result=$mBanner->togStatus($id);
194         if($result)
195             return api_success("操作成功!");
196         else
197             return api_error();
198         
199 
200     
201 
202     /**  测试
203      * @return array|false|\PDOStatement|string|\think\Model
204      */
205     function f()
206         $id = input(‘param.‘);
207         $banner = new ModelBanner();
208         $item = $banner->find($id);
209         return $item;
210     
211 
212     public function image()
213 //        $arr = input(‘param.‘);
214 //        dump($arr);
215 //        return $arr;
216         return input(‘param.‘);
217         return $_POST[‘title‘];
218         return $_FILES;
219         $files = request()->file(‘file‘);
220         return $files;
221     
222 
223 

 

 

 

待更新 ...

 

以上是关于准备工作数据库校验用户准备工作的主要内容,如果未能解决你的问题,请参考以下文章

Linux驱动学习之驱动开发准备工作

drf 权限校验设置与源码分析

Vue2+VueRouter2+webpack 构建项目实战准备工作

技术准备工作

微信开发(准备工作简版)

类加载器工作机制