前言

学习UI自动化的同学都应该知道PO模式,PO共分为三层,分别是页面定位层,页面对象层,业务逻辑层。

po模式有以下几个优点

1.易读性好

2.扩展性高

3.复用性强

4.维护性好

5.代码冗余率低

前因:让不会代码的同学也能编写自动化
思考问题:市面上不乏有录制回放,数据驱动的框架,为什么还要自己封装呢
解决问题:封装能更加贴切自己公司的项目,能更好的进行扩展,而且更能展示自身的价值

这里我就不具体讲解selenium基础方法的封装了,和PO模式一样的,没有做很大的改动

源码:https://download.csdn.net/download/qq_36076898/15724810

一.用例设计(Excel)

1.Excel sheet设计

参数说明:用于生成测试数据,数据生成通过tool自动生成。后面会具体讲解怎么使用


定位:所有的元素定位。后面会具体讲解怎么使用

登录信息配置:环境、账号的配置。后面会具体讲解怎么使用

用例:测试用例,我这里就只写了个demo。后面会仔细剖析用例

二.代码封装

1.封装读取【参数说明】sheet的代码

import random
import string
import time
import datetimeclass Tool:@staticmethoddef get_random_str(random_length=6):"""生成一个指定长度的随机字符串,数字字母混合string.digits=0123456789string.ascii_letters=abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"""str_list = [random.choice(string.digits + string.ascii_letters) for i in range(random_length)]random_str = ''.join(str_list)return random_str@staticmethoddef get_random_letters(random_length=6):"""生成一个指定长度的随机字符串,纯字母string.ascii_letters=abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"""str_list = [random.choice(string.ascii_letters) for i in range(random_length)]random_letters = ''.join(str_list)return random_letters@staticmethoddef get_time_stamp(unit='s'):"""获取时间戳:param unit: 单位   s 秒,ms 毫秒:return:"""data = time.time()if unit == 's':result = int(data)elif unit == 'ms':result = int(round(data * 1000))else:result = datareturn result@staticmethoddef get_date():"""获取当前时间:return:"""result = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')return result@staticmethoddef get_phone_number(key=False):"""随机生成电话号码:param key: 默认生成虚假手机号,True 生成真实手机号:return:"""pre_list = ["130", "131", "132", "133", "134", "135", "136", "137", "138", "139","147", "150", "151", "152", "153", "155", "156", "157", "158", "159","186", "187", "188", "189"]if key:result = random.choice(pre_list) + "".join(random.choice("0123456789") for i in range(8))else:result = random.choice(pre_list) + "".join(random.choice("0123456789") * 4) + "".join(random.choice("0123456789") * 4)return result@staticmethoddef get_email():"""生成随机邮箱"""# 用数字0-9 和字母a-z 生成随机邮箱。list_sum = [i for i in range(10)] + ["a", "b", "c", "d", "e", "f", "g", "h", 'i', "j", "k","l", "M", "n", "o", "p", "q", "r", "s", "t", "u", "v","w", "x", "y", "z"]email_str = ""# email_suffix = ["@163.com", "@qq.com", "@gmail.com", "@mail.hk.com", "@yahoo.co.id", "mail.com"]email_suffix = ["@163.com", "@qq.com"]for i in range(10):a = str(random.choice(list_sum))email_str = email_str + a# 随机拼接不同的邮箱后缀return email_str + random.choice(email_suffix)@staticmethoddef get_parameter(data):"""判断某一条excel数据中书否存在参数,若存在直接替换掉:param data::return:"""value = data.replace('#', '')if 'get_time_stamp_ms' in value:test_data = getattr(Tool, value.replace('_ms', ''))('ms')elif 'get_time_stamp_s' in value:test_data = getattr(Tool, value.replace('_second', ''))()else:if hasattr(Tool, value):test_data = getattr(Tool, value)()else:test_data = valuereturn test_dataif __name__ == '__main__':test = Tool()print(test.get_parameter('#get_time_stamp_second#'))

这里主要讲解下get_parameter()方法,其他方法都是生成数据的。get_parameter()方法处理用例中传的参数,根据传方法名生成想要的数据。例如:测过程中需要时间格式的数据,那么在测试用例中就可以填写:#get_date#

2.封装读取【定位】sheet的代码

import xlrd
from common.log import Loglog = Log()class ReadExcel:def __init__(self, fileName):"""new_data是最后返回的值config.project_path.replace('\\', '/') + "/data.xlsx":param fileName: excel文件名路径"""self.fileName = fileName# 读取excel文件夹self.book = xlrd.open_workbook(self.fileName)@staticmethoddef data_type(test_type, test_value):"""判断从excel单元格中获取的数据类型1 string(text), 2 number, 3 date, 4 boolean:param test_type: 类型:param test_value: 值:return:"""if test_type == 1:"""字符串"""return test_valueelif test_type == 2:if '.0' in str(test_value):"""整数"""return int(test_value)else:"""浮点"""return test_valueelif test_type == 3:"""日期"""date = xlrd.xldate_as_datetime(test_value, 0).strftime('%Y-%m-%d')return dateelif test_type == 4:"""布尔类型"""if test_value == 1:return Trueelif test_value == 0:return Falsedef stitching_element(self, sheet_name='定位'):"""读取excel,处理元素数据:param sheet_name::return:"""result = {}# 读取excelsheet = self.book.sheet_by_name(sheet_name)row_value_one = sheet.row_values(0)  # 第一行# 总行数row_num = sheet.nrowsfor i in range(1, row_num):result[sheet.cell_value(i, row_value_one.index('功能'))] = sheet.cell_value(i, row_value_one.index('元素定位'))return result

结果如下:返回字典,【功能】作为key,【元素定位】作为vlaue

{'登录-用户名': '//*[@placeholder="请输入内容"]', '登录-密码': '//*[@placeholder="请输入密码"]','登录-登录按钮': '//span[text()=" 登录 "]', '登录-错误提示': '//p[text()="用户名或者密码错误"]','首页-昵称': '//*[@id="app"]/section/header/div[2]/span', '首页-退出登录': '//li[text()="退出登录"]','首页-一级菜单': '//li[@role="menuitem"]/div/span', '首页-用户管理': '//span[text()="用户管理"]','首页-用户管理-用户列表': '//li/span[text()="用户列表"]', '首页-用户管理-用户列表-添加用户': '//button/span[text()="添加用户"]','首页-用户管理-用户列表-添加用户-用户名': '//label[text()="用户名"]/following-sibling::div[1]/div/input','首页-用户管理-用户列表-添加用户-昵称': '//label[text()="昵称"]/following-sibling::div[1]/div/input','首页-用户管理-用户列表-添加用户-邮箱': '//label[text()="邮箱"]/following-sibling::div[1]/div/input','首页-用户管理-用户列表-添加用户-手机号': '//label[text()="手机号"]/following-sibling::div[1]/div/input','首页-用户管理-用户列表-添加用户-密码': '//label[text()="密码"]/following-sibling::div[1]/div/input','首页-用户管理-用户列表-添加用户-确定': '//*[@id="app"]/section/section/main/div/div[4]/div/div[4]/div/div[3]/span/button[2]/span','首页-用户管理-用户列表-当前页的所有用户': '//tr/td[2]/div'}

3.封装读取【登录信息配置】sheet的代码

import xlrd
from common import config
from common.log import Loglog = Log()class ReadExcel:def __init__(self, fileName):"""new_data是最后返回的值config.project_path.replace('\\', '/') + "/data.xlsx":param fileName: excel文件名路径"""self.fileName = fileName# 读取excel文件夹self.book = xlrd.open_workbook(self.fileName)@staticmethoddef data_type(test_type, test_value):"""判断从excel单元格中获取的数据类型1 string(text), 2 number, 3 date, 4 boolean:param test_type: 类型:param test_value: 值:return:"""if test_type == 1:"""字符串"""return test_valueelif test_type == 2:if '.0' in str(test_value):"""整数"""return int(test_value)else:"""浮点"""return test_valueelif test_type == 3:"""日期"""date = xlrd.xldate_as_datetime(test_value, 0).strftime('%Y-%m-%d')return dateelif test_type == 4:"""布尔类型"""if test_value == 1:return Trueelif test_value == 0:return Falsedef processing_environment(self, sheet_name='登录信息配置'):"""处理环境和账号"""result = {}sheet = self.book.sheet_by_name(sheet_name)cell_values = sheet.merged_cellsrow_value_one = sheet.row_values(3)cell_range = []for cell_value in cell_values:for i in range(cell_value[0], cell_value[1]):for j in range(cell_value[2], cell_value[3]):if '是' == sheet.cell_value(i, j):cell_range.append(cell_value)breakresult['url'] = sheet.cell_value(cell_range[0][0], row_value_one.index('url'))for i in range(cell_range[0][0], cell_range[0][1]):if i == cell_range[0][0]:result['account'] = {"username": sheet.cell_value(i, row_value_one.index('用户名')),"nickname": sheet.cell_value(i, row_value_one.index('昵称')),"password": self.data_type(2, sheet.cell_value(i, row_value_one.index('密码')))}result[sheet.cell_value(i, row_value_one.index('用户名'))] = {"nickname": sheet.cell_value(i, row_value_one.index('昵称')),"password": self.data_type(2, sheet.cell_value(i, row_value_one.index('密码')))}result['username_text'] = sheet.cell_value(0, 1)result['password_text'] = sheet.cell_value(1, 1)result['login_button'] = sheet.cell_value(2, 1)return result

结果如下:

{'url': 'http://127.0.0.1/#/login',
'account': {'username': 'test1', 'nickname': '测试', 'password': 123456},
'test1': {'nickname': '测试', 'password': 123456},
'root': {'nickname': '超级管理员', 'password': 123456},
'username_text': '登录-用户名',
'password_text': '登录-密码',
'login_button': '登录-登录按钮'
}

4.封装读取【demo】(用例)sheet的代码

import xlrd
from common import config
from common.log import Loglog = Log()class ReadExcel:def __init__(self, fileName):"""new_data是最后返回的值config.project_path.replace('\\', '/') + "/data.xlsx":param fileName: excel文件名路径"""self.fileName = fileName# 读取excel文件夹self.book = xlrd.open_workbook(self.fileName)@staticmethoddef data_type(test_type, test_value):"""判断从excel单元格中获取的数据类型1 string(text), 2 number, 3 date, 4 boolean:param test_type: 类型:param test_value: 值:return:"""if test_type == 1:"""字符串"""return test_valueelif test_type == 2:if '.0' in str(test_value):"""整数"""return int(test_value)else:"""浮点"""return test_valueelif test_type == 3:"""日期"""date = xlrd.xldate_as_datetime(test_value, 0).strftime('%Y-%m-%d')return dateelif test_type == 4:"""布尔类型"""if test_value == 1:return Trueelif test_value == 0:return Falsedef stitching_data(self, case_type='冒烟'):"""读取excel,处理用例数据:return:"""result = []# 获取所有sheetsheets = self.book.sheet_names()for sheet_name in sheets:if sheet_name != '参数说明' and sheet_name != '定位' and sheet_name != '登录信息配置':sheet = self.book.sheet_by_name(sheet_name)# 用例数量case_num_list = sheet.merged_cells[0:len(sheet.merged_cells) // 3]# 获取第行列数据row_value_one = sheet.row_values(0)  # 第一行# 处理数据for i in case_num_list:tag = sheet.cell_value(i[0], row_value_one.index('用例类型'))if case_type == tag or case_type is True:case = {'description': sheet.cell_value(i[0], row_value_one.index('描述')), 'tag': tag}step = []for j in range(i[0], i[1]):step.append({'element': sheet.cell_value(j, row_value_one.index('定位')),'operate': sheet.cell_value(j, row_value_one.index('操作方式')),'data': sheet.cell_value(j, row_value_one.index('测试数据')),'result': sheet.cell_value(j, row_value_one.index('参数标签'))})case['step'] = stepresult.append(case)return result
[
{'description': '验证超级管理员能否创建用户','tag': '冒烟','step': [{'element': '', 'operate': '登录', 'data': '#root#', 'result': ''}, {'element': '首页-用户管理', 'operate': '单击', 'data': '', 'result': ''}, {'element': '首页-用户管理-用户列表', 'operate': '单击', 'data': '', 'result': ''},         {'element': '首页-用户管理-用户列表-添加用户', 'operate': '单击', 'data': '', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-用户名', 'operate': '输入', 'data': '#get_random_letters#', 'result': '期望结果'}, {'element': '首页-用户管理-用户列表-添加用户-昵称', 'operate': '输入', 'data': '#get_random_letters#', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-邮箱', 'operate': '输入', 'data': '#get_email#', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-手机号', 'operate': '输入', 'data': '#get_phone_number#', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-密码', 'operate': '输入', 'data': '#get_random_letters#', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-确定', 'operate': '单击', 'data': '', 'result': ''}, {'element': '首页-用户管理-用户列表-当前页的所有用户', 'operate': '获取多个文本', 'data': '', 'result': '实际结果'},{'element': '', 'operate': '期望结果in实际结果', 'data': '', 'result': ''}]},{'description': '验证超级管理员能否创建用户',
'tag': '冒烟',
'step': [{'element': '', 'operate': '登录', 'data': '#root#', 'result': ''},{'element': '首页-用户管理', 'operate': '单击', 'data': '', 'result': ''},{'element': '首页-用户管理-用户列表', 'operate': '单击', 'data': '', 'result': ''},{'element': '首页-用户管理-用户列表-添加用户', 'operate': '单击', 'data': '', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-用户名', 'operate': '输入', 'data': '#get_random_letters#', 'result': '期望结果'}, {'element': '首页-用户管理-用户列表-添加用户-昵称', 'operate': '输入', 'data': '#get_random_letters#', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-邮箱', 'operate': '输入', 'data': '#get_email#', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-手机号', 'operate': '输入', 'data': '#get_phone_number#', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-密码', 'operate': '输入', 'data': '#get_random_letters#', 'result': ''}, {'element': '首页-用户管理-用户列表-添加用户-确定', 'operate': '单击', 'data': '', 'result': ''}, {'element': '首页-用户管理-用户列表-当前页的所有用户', 'operate': '获取多个文本', 'data': '', 'result': '实际结果'}, {'element': '', 'operate': '期望结果in实际结果', 'data': '', 'result': ''}]}
]

三.用例剖析以及设计思路讲解


代码会自动遍历获取所有sheet,【参数说明】【定位】【登录信息配置】除外,将所获取到的用例进行数据组装。


用例步骤:这个应该都会写,具体到每一步做什么。最后需要加一步这条用例通过的判断标准即可

操作方式:对应步骤所作出的操作,比如:单击、输入等,主要说一下第一步和最后一步所对应的操作。第一步操作如果是【登录】,操作方式直接填【登录】(这里登录有三步,输入用户名、输入密码、点击登录,我直接封装在代码里了,减少excel重复添加。如果有验证码,可以自行添加封装);最后一步通过方式必须按照【期望结果in实际结果】此格式书写,【期望结果in实际结果】代表期望结果在实际结果中,【期望结果=实际结果】代表期望结果等于实际结果

定位:根据【定位】sheet中【功能】写

测试数据:某一步骤需要说明数据,直接填写对应的方法即可,参考【参数说明】sheet;也可直接填写【参数标签】里所需要的参数

参数标签:主要用户获取期望结果、实际结果、后面步骤可能需要之前某个步骤的数据。

用例类型:方便执行脚本的时候区分执行那类用例

import ddt
import unittest
from common.log import Log
from common.read_excel import ReadExcel
from common.base import Base
from common.tool import Tool
from runTest import run_parsertool = Tool()
log = Log()
case_info = run_parser()
read_excel = ReadExcel(case_info['file_name'])
case = case_info['case']
login_info = read_excel.processing_environment()@ddt.ddt
class TestAllCase(unittest.TestCase):@classmethoddef setUpClass(cls) -> None:log.info('<------------------用例执行开始------------------>')def setUp(self) -> None:self.case_name = self._testMethodName  # 获取执行当前用例的 方法名self.case = Base()log.info('--------开始{}用例--------'.format(self.case_name))def tearDown(self) -> None:self.case.quit()log.info('--------结束{}用例--------\n'.format(self.case_name))@classmethoddef tearDownClass(cls) -> None:log.info('<------------------用例执行结束------------------>')@ddt.data(*case)def test_speed(self, data):log.info(data['description'])test_data = {}  # 测试过程中需要的数据desired_result = []  # 期望结果actual_result = []  # 实际结果for i in data['step']:if i['operate'] == '登录':# 登录self.case.open(login_info['url'])if i['data'] == '':self.case.send_keys(element=login_info['username_text'],text=login_info['account']['username'])self.case.send_keys(element=login_info['password_text'],text=login_info['account']['password'])else:self.case.send_keys(element=login_info['username_text'],text=i['data'].replace('#', ''))self.case.send_keys(element=login_info['password_text'],text=login_info[i['data'].replace('#', '')]['password'])self.case.click(element=login_info['login_button'])elif '期望结果' in i['operate'] or '实际结果' in i['operate']:# 断言log.info("期望结果:{}".format(desired_result[0]))log.info("实际结果:{}".format(actual_result[0]))if '=' in i['operate']:self.assertEqual(desired_result[0], actual_result[0])elif 'in' in i['operate']:self.assertTrue(desired_result[0] in actual_result[0])else:# 其他操作if i['operate'] == '单击':self.case.click(element=i['element'])elif i['operate'] == '输入':input_data = tool.get_parameter(i['data'])if input_data in test_data.keys():self.case.send_keys(element=i['element'], text=test_data[input_data])else:self.case.send_keys(element=i['element'], text=input_data)if i['result'] != '' and i['result'] != '期望结果':# 将复用的参数加入test_data[i['result']] = input_dataelif i['result'] == '期望结果':# 将期望结果加入desired_result.append(input_data)elif i['operate'] == '获取多个文本':actual_result.append(self.case.get_texts(element=i['element']))elif i['operate'] == '获取单个文本':actual_result.append(self.case.get_text(element=i['element']))elif i['operate'] == '悬停':self.case.mouse_flight(element=i['element'])

四.批量运行生成报告

import sys
import unittest
import ossys.path.append('../')
from common.log import Log
from common import config
from common.HTMLTestRunner_cn import HTMLTestRunner
from common.Email import SendMail
from common.read_excel import ReadExcel
import argparselog = Log()
send_report_path = config.send_report_path
reportTitle = config.reportTitle
description = config.descriptiondef run_parser():"""参数运行"""parser = argparse.ArgumentParser(description='通过cmd传入参数执行')parser.add_argument('-t', '--case', default=True, help='用例类型')parser.add_argument('-f', '--fileName', default=config.project_path.replace('\\', '/') + "/data.xlsx", help='用例类型')args_case = parser.parse_args()case_type = args_case.casefile_name = args_case.fileNameread_excel = ReadExcel(fileName=file_name)case_info = read_excel.stitching_data(case_type)return {'case': case_info, "file_name": file_name}def add_case():"""加载所有的测试用例"""# test_suite = unittest.defaultTestLoader.discover(start_dir=path, pattern=pattern, top_level_dir=None)test_cases = unittest.TestSuite()discover = unittest.defaultTestLoader.discover(start_dir=config.case_path, pattern='test*.py')for test_suite in discover:for test_case in test_suite:# 添加用例到test_casestest_cases.addTests(test_case)log.info('测试用例总数:{}'.format(test_cases._tests.__len__()))return test_casesdef run_html(test_suit):"""执行测试用例,生成报告"""# 判断存储报告的路径是否存在if os.path.exists(config.report_path) is False:os.makedirs(config.report_path)with open(send_report_path, 'wb') as f:runner = HTMLTestRunner(stream=f, title=reportTitle,description=description,verbosity=2, retry=1, save_last_try=True)runner.run(test_suit)SendMail().send()def run():"""运行"""# 运行命令:python runTest.py  -c 功能 -f excel文件路径cases = add_case()run_html(cases)if __name__ == '__main__':run()

1.命令运行指定类型用例

python runTest.py  -t 冒烟 -f C:\Users\admin\Desktop\data.xlsx

-t:用例类型
-f:excel路径

上面命令含义:读取C:\Users\admin\Desktop\data.xlsx用例,执行冒烟测试用例

2.命令所有类型用例

默认执行全部用例

python runTest.py  -f C:\Users\admin\Desktop\data.xlsx

五.调试用例

1.将用例里的任务类型随便写一个关键字:test

2.将run文件里的代码作出相应修改

3.执行run()方法

后言

以上就是我的设计思路,如若有什么问题欢迎给位留言!目前只封装了几个常见的操作,后面若需要其他操作的时候再封装!

python+unittest框架 UI自动化设计思路以及代码剖析,增加易用性相关推荐

  1. python安卓自动化实现方法_uiautomator +python 实现安卓UI自动化

    简单实例 注:安卓6.0以上的手机不会自动安装app-uiautomator.apk和app-uiautomator-test.apk,需要手动安装,否则报错ioerror RPC server no ...

  2. pythonapp自动化_基于python的App UI自动化环境搭建

    Android端Ui 自动化环境搭建 一,安装JDK.SDK 二,添加环境变量 Widows: 1.系统变量→新建 JAVA_HOME 变量 E:\Java\jdk1.7.0 jdk安装目录 2.系统 ...

  3. Python实现APP UI自动化以及OpenCV图像识别元素

    OpenCV图像识别元素代码 # -*- encoding=utf-8 -*-__author__ = 'Jeff.xie'import cv2 import sysdef _tran_canny(i ...

  4. 普歌-允异团队-【Java实例】一起做一个简单的王者荣耀RPG吧!从设计思路到代码实现一条龙!-登录与注册(IO流)/记录时间/属性面板呈现

    [Java实例]-王者荣耀RPG-从设计思路到代码实现 前言 一.案例分析 1. 设计理念 2. 功能设计 (1)登录与注册 (2)游戏时间记录 (3)选择游戏模式 (4)游戏地图 (5)生物属性 二 ...

  5. 【三子棋小游戏的设计思路以及代码实现】

    目录 一,设计思路 二, 代码实现 首先,欢迎各位点进我的文章.话不多说,正如标题一样,接下来,我来给大家讲解三子棋小游戏的设计思路以及代码实现.            一,设计思路 一. 大家应该都 ...

  6. AT串口抽象层的设计思路及代码实现

    文章目录 1 AT串口抽象层的设计思路及代码实现 1.1 AT串口抽象层的设计思路 1.2 AT串口抽象层的代码实现 1 AT串口抽象层的设计思路及代码实现 1.1 AT串口抽象层的设计思路 我们先来 ...

  7. ML之分类预测:机器学习中多分类预测数据集可视化(不同类别赋予不同颜色)设计思路及代码实现

    ML之分类预测:机器学习中多分类预测数据集可视化(不同类别赋予不同颜色)设计思路及代码实现 目录 机器学习中多分类预测数据集可视化(不同类别赋予不同颜色)设计思路及代码实现 代码实现

  8. python接口测试框架与自动化实战_Python接口自动化从设计到开发,测试框架实战与自动化进阶视频课程...

    Python接口自动化从设计到开发,测试框架实战与自动化进阶视频课程21套高级软件测试,性能测试,功能测试,自动化测试,接口测试,移动端测试,手机测试,WEB测试,渗透测试,测试用例设计,黑盒测试,白 ...

  9. Unittest单元测试框架UI自动化

    今天我们讲解在python中如何使用unittest框架实现UI自动化,对于如何使用webdriver的API进行网页的操作今天不再赘述,错过的小伙伴请戳下方链接直达~~ 1.unittest单元测试 ...

最新文章

  1. parted新建分区_扩展分区及文件系统(Linux)
  2. 5.25上午 外教专业课 听力
  3. bs4爬取的时候有两个标签相同_PYTHON爬取数据储存到excel
  4. 无代码iVX编程实现简单 小蜜蜂 经典游戏
  5. css动画-模拟正余弦曲线
  6. 简述单片微型计算机屏蔽的作用,单片机原理及应用试题库 - 答案
  7. 73页PPT,教你从0到1构建用户画像系统(附下载)
  8. python创建maven工程_Maven项目
  9. 解决LInux更新慢的问题, 更换国内软件源
  10. 海洋工作室——网站建设专家:全数据库比较工具
  11. CGAL Catmull-Clark Subdivide Surface
  12. 昆仑通态复制的程序可以用吗_MCGS昆仑通态触摸屏常见问题(5)
  13. C#开发工厂ERP生产管理系统源码
  14. linux 如何重建mbr,重建mbr要不要勾选
  15. 心电信号质量评估——ecg_qc工具包介绍(二)
  16. 类对象初始化和Initializer_list的
  17. hbuilder创建app并利用真机运行调试
  18. Kubernetes Events介绍(下)
  19. ps、markman、cutterman下载地址
  20. 热力学分布用matlab,matlab在热物理学中的应用.doc

热门文章

  1. 制图操作案例:ArcGIS Pro制图及出图小技巧——以土地利用图为例
  2. 代码下载淘宝天猫的产品主图视频和详情页视频
  3. Python最强装逼神技!微信远程控制电脑,想让你电脑关机就关机!
  4. 如何设计属于跨境品牌风格的独立站
  5. javascript 在conductor的使用
  6. shell编程中declare命令的使用
  7. 知虾:虾皮Shopee换货卖家要怎么处理?
  8. VS2013中的调试程序的方法
  9. SSM二十大新闻网站系统-计算机毕设 附源码96856
  10. latex name标注作者时怎么标注通讯作者,如何加*corresponding author脚标