基于Python的HTTP接口自动化测试框架实现

news2024/9/23 3:24:50

对服务后台一系列的http接口功能测试。

输入:根据接口描述构造不同的参数输入值

输出:XML文件

二、实现方法

1、选用Python脚本来驱动测试

2、采用Excel表格管理测试数据,包括用例的管理、测试数据录入、测试结果显示等等,这个需要封装一个Excel的类即可。

3、调用http接口采用Python封装好的API即可

4、测试需要的http组装字符转处理即可

5、设置2个检查点,XML文件中的返回值字段(通过解析XML得到);XML文件的正确性(文件对比)

6、首次执行测试采用半自动化的方式,即人工检查输出的XML文件是否正确,一旦正确将封存XML文件,为后续回归测试的预期结果,如果发现错误手工修正为预期文件。(注意不是每次测试都人工检查该文件,只首次测试的时候才检查)

三、Excel表格样式

  1. ​现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。

  2. 如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受

  3. 可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛

  4. 分享他们的经验,还会分享很多直播讲座和技术沙龙

  5. 可以免费学习!划重点!开源的!!!

  6. qq群号:680748947【暗号:csdn11】

四、实现代码(代码才是王道,有注释很容易就能看明白的)

1、测试框架代码

  1. # -*- coding: utf-8 -*-

  2. #****************************************************************

  3. # TestFrame.py

  4. # Author : Vince

  5. # Version : 1.1.2

  6. # Date : 2011-3-14

  7. # Description:

  8. # 平台:自动化测试平台

  9. # 简介:用于服务端自动化测试,该模块为测试平台主体,包含了执行自动化测试所需要的库函数及相关框架,此外还包含了Excel标准模版。

  10. # 使用约束:输入数据必须符合Excel标准模版;测试脚本编写遵循自动化测试框架规范(具体参见样例)

  11. #

  12. # 功能实现列表:

  13. # 1、Excel操作封装

  14. # 2、参数组装

  15. # 3、测试调用

  16. # 4、测试报告保存

  17. #

  18. # 适用范围:

  19. # 1、典型输入输出测试接口的测试,输入输出为一具体值,输入可以为多个参数,输出暂时支持1个输出,后期可考虑支持多输出的判断;

  20. # 2、输出返回XML文件,解析XML判断测试结果;

  21. # 3、输出为一个压缩包,解压包判断测试结果;

  22. # 4、......

  23. #

  24. # 版本修改历史

  25. # 版本号 修改人 修改时间 修改说明

  26. # 1.0.0 Vince 2011-2-18 创建

  27. # 1.1.0 Vince 2011-3-1 修改操作EXCEL类 create_excel_obj

  28. # 1.1.1 Vince 2011-3-8 增加解析XML函数

  29. # 1.1.2 Vince 2011-3-14 统一注释

  30. # 1.2.0 Vince 2011-3-18 修改了Excel模版样式,增加了返回值中同时检查多个Item及结构体的函数

  31. # 1.3.0 Vince 2011-3-19 Excel对象操作由原来使用xlrd改用win32com,修改了create_excel类

  32. # 1.4.0 Vince 2011-3-20 增强Excel显示的可读性;增加用例执行控制;增加用例执行统计功能;改善测试框架性能(提升1.5倍)

  33. # 1.5.0 Vince 2011-3-22 将Win32com访问Excel表格由MS Excel改为WPS ET

  34. # 1.5.1 Vince 2011-3-28 check_struct_item中调用check_item函数修改

  35. #****************************************************************

  36. import os,sys, urllib, httplib, profile, datetime, time

  37. from xml2dict import XML2Dict

  38. import win32com.client

  39. from win32com.client import Dispatch

  40. #Excel表格中测试结果底色

  41. OK_COLOR=0xffffff

  42. NG_COLOR=0xff

  43. #NT_COLOR=0xffff

  44. NT_COLOR=0xC0C0C0

  45. #Excel表格中测试结果汇总显示位置

  46. TESTTIME=[1, 14]

  47. TESTRESULT=[2, 14]

  48. #Excel模版设置

  49. #self.titleindex=3 #Excel中测试用例标题行索引

  50. #self.casebegin =4 #Excel中测试用例开始行索引

  51. #self.argbegin =2 #Excel中参数开始列索引

  52. #self.argcount =5 #Excel中支持的参数个数

  53. #self.resultcol =28 #TestResult列

  54. class create_excel:

  55. def __init__(self, sFile, dtitleindex=3, dcasebegin=4, dargbegin=3, dargcount=5, dresultcol=28):

  56. self.xlApp = win32com.client.Dispatch('et.Application') #MS:Excel WPS:et

  57. try:

  58. self.book = self.xlApp.Workbooks.Open(sFile)

  59. except:

  60. print_error_info()

  61. print "打开文件失败"

  62. exit()

  63. self.file=sFile

  64. self.titleindex=dtitleindex

  65. self.casebegin=dcasebegin

  66. self.argbegin=dargbegin

  67. self.argcount=dargcount

  68. self.resultcol=dresultcol

  69. self.allresult=[]

  70. def close(self):

  71. #self.book.Close(SaveChanges=0)

  72. self.book.Save()

  73. self.book.Close()

  74. #self.xlApp.Quit()

  75. del self.xlApp

  76. def read_data(self, iSheet, iRow, iCol):

  77. try:

  78. sht = self.book.Worksheets(iSheet)

  79. sValue=str(sht.Cells(iRow, iCol).Value)

  80. except:

  81. self.close()

  82. print('读取数据失败')

  83. exit()

  84. #去除'.0'

  85. if sValue[-2:]=='.0':

  86. sValue = sValue[0:-2]

  87. return sValue

  88. def write_data(self, iSheet, iRow, iCol, sData, color=OK_COLOR):

  89. try:

  90. sht = self.book.Worksheets(iSheet)

  91. sht.Cells(iRow, iCol).Value = sData.decode("utf-8")

  92. sht.Cells(iRow, iCol).Interior.Color=color

  93. self.book.Save()

  94. except:

  95. self.close()

  96. print('写入数据失败')

  97. exit()

  98. #获取用例个数

  99. def get_ncase(self, iSheet):

  100. try:

  101. return self.get_nrows(iSheet)-self.casebegin+1

  102. except:

  103. self.close()

  104. print('获取Case个数失败')

  105. exit()

  106. def get_nrows(self, iSheet):

  107. try:

  108. sht = self.book.Worksheets(iSheet)

  109. return sht.UsedRange.Rows.Count

  110. except:

  111. self.close()

  112. print('获取nrows失败')

  113. exit()

  114. def get_ncols(self, iSheet):

  115. try:

  116. sht = self.book.Worksheets(iSheet)

  117. return sht.UsedRange.Columns.Count

  118. except:

  119. self.close()

  120. print('获取ncols失败')

  121. exit()

  122. def del_testrecord(self, suiteid):

  123. try:

  124. #为提升性能特别从For循环提取出来

  125. nrows=self.get_nrows(suiteid)+1

  126. ncols=self.get_ncols(suiteid)+1

  127. begincol=self.argbegin+self.argcount

  128. #提升性能

  129. sht = self.book.Worksheets(suiteid)

  130. for row in range(self.casebegin, nrows):

  131. for col in range(begincol, ncols):

  132. str=self.read_data(suiteid, row, col)

  133. #清除实际结果[]

  134. startpos = str.find('[')

  135. if startpos>0:

  136. str = str[0:startpos].strip()

  137. self.write_data(suiteid, row, col, str, OK_COLOR)

  138. else:

  139. #提升性能

  140. sht.Cells(row, col).Interior.Color = OK_COLOR

  141. #清除TestResul列中的测试结果,设置为NT

  142. self.write_data(suiteid, row, self.resultcol, 'NT', NT_COLOR)

  143. except:

  144. self.close()

  145. print('清除数据失败')

  146. exit()

  147. #执行调用

  148. def run_case(IPPort, url):

  149. conn = httplib.HTTPConnection(IPPort)

  150. conn.request("GET", url)

  151. rsps = conn.getresponse()

  152. data = rsps.read()

  153. conn.close()

  154. return data

  155. #获取用例基本信息[Interface,argcount,[ArgNameList]]

  156. def get_caseinfo(Data, SuiteID):

  157. caseinfolist=[]

  158. sInterface=Data.read_data(SuiteID, 1, 2)

  159. argcount=int(Data.read_data(SuiteID, 2, 2))

  160. #获取参数名存入ArgNameList

  161. ArgNameList=[]

  162. for i in range(0, argcount):

  163. ArgNameList.append(Data.read_data(SuiteID, Data.titleindex, Data.argbegin+i))

  164. caseinfolist.append(sInterface)

  165. caseinfolist.append(argcount)

  166. caseinfolist.append(ArgNameList)

  167. return caseinfolist

  168. #获取输入

  169. def get_input(Data, SuiteID, CaseID, caseinfolist):

  170. sArge=''

  171. #参数组合

  172. for j in range(0, caseinfolist[1]):

  173. sArge=sArge+caseinfolist[2][j]+'='+Data.read_data(SuiteID, Data.casebegin+CaseID, Data.argbegin+j)+'&'

  174. #去掉结尾的&字符

  175. if sArge[-1:]=='&':

  176. sArge = sArge[0:-1]

  177. sInput=caseinfolist[0]+sArge #组合全部参数

  178. return sInput

  179. #结果判断

  180. def assert_result(sReal, sExpect):

  181. sReal=str(sReal)

  182. sExpect=str(sExpect)

  183. if sReal==sExpect:

  184. return 'OK'

  185. else:

  186. return 'NG'

  187. #将测试结果写入文件

  188. def write_result(Data, SuiteId, CaseId, resultcol, *result):

  189. if len(result)>1:

  190. ret='OK'

  191. for i in range(0, len(result)):

  192. if result[i]=='NG':

  193. ret='NG'

  194. break

  195. if ret=='NG':

  196. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,ret, NG_COLOR)

  197. else:

  198. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,ret, OK_COLOR)

  199. Data.allresult.append(ret)

  200. else:

  201. if result[0]=='NG':

  202. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], NG_COLOR)

  203. elif result[0]=='OK':

  204. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], OK_COLOR)

  205. else: #NT

  206. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], NT_COLOR)

  207. Data.allresult.append(result[0])

  208. #将当前结果立即打印

  209. print 'case'+str(CaseId+1)+':', Data.allresult[-1]

  210. #打印测试结果

  211. def statisticresult(excelobj):

  212. allresultlist=excelobj.allresult

  213. count=[0, 0, 0]

  214. for i in range(0, len(allresultlist)):

  215. #print 'case'+str(i+1)+':', allresultlist[i]

  216. count=countflag(allresultlist[i],count[0], count[1], count[2])

  217. print 'Statistic result as follow:'

  218. print 'OK:', count[0]

  219. print 'NG:', count[1]

  220. print 'NT:', count[2]

  221. #解析XmlString返回Dict

  222. def get_xmlstring_dict(xml_string):

  223. xml = XML2Dict()

  224. return xml.fromstring(xml_string)

  225. #解析XmlFile返回Dict

  226. def get_xmlfile_dict(xml_file):

  227. xml = XML2Dict()

  228. return xml.parse(xml_file)

  229. #去除历史数据expect[real]

  230. def delcomment(excelobj, suiteid, iRow, iCol, str):

  231. startpos = str.find('[')

  232. if startpos>0:

  233. str = str[0:startpos].strip()

  234. excelobj.write_data(suiteid, iRow, iCol, str, OK_COLOR)

  235. return str

  236. #检查每个item (非结构体)

  237. def check_item(excelobj, suiteid, caseid,real_dict, checklist, begincol):

  238. ret='OK'

  239. for checkid in range(0, len(checklist)):

  240. real=real_dict[checklist[checkid]]['value']

  241. expect=excelobj.read_data(suiteid, excelobj.casebegin+caseid, begincol+checkid)

  242. #如果检查不一致测将实际结果写入expect字段,格式:expect[real]

  243. #将return NG

  244. result=assert_result(real, expect)

  245. if result=='NG':

  246. writestr=expect+'['+real+']'

  247. excelobj.write_data(suiteid, excelobj.casebegin+caseid, begincol+checkid, writestr, NG_COLOR)

  248. ret='NG'

  249. return ret

  250. #检查结构体类型

  251. def check_struct_item(excelobj, suiteid, caseid,real_struct_dict, structlist, structbegin, structcount):

  252. ret='OK'

  253. if structcount>1: #传入的是List

  254. for structid in range(0, structcount):

  255. structdict=real_struct_dict[structid]

  256. temp=check_item(excelobj, suiteid, caseid,structdict, structlist, structbegin+structid*len(structlist))

  257. if temp=='NG':

  258. ret='NG'

  259. else: #传入的是Dict

  260. temp=check_item(excelobj, suiteid, caseid,real_struct_dict, structlist, structbegin)

  261. if temp=='NG':

  262. ret='NG'

  263. return ret

  264. #获取异常函数及行号

  265. def print_error_info():

  266. """Return the frame object for the caller's stack frame."""

  267. try:

  268. raise Exception

  269. except:

  270. f = sys.exc_info()[2].tb_frame.f_back

  271. print (f.f_code.co_name, f.f_lineno)

  272. #测试结果计数器,类似Switch语句实现

  273. def countflag(flag,ok, ng, nt):

  274. calculation = {'OK':lambda:[ok+1, ng, nt],

  275. 'NG':lambda:[ok, ng+1, nt],

  276. 'NT':lambda:[ok, ng, nt+1]}

  277. return calculation[flag]()

2、项目测试代码

  1. # -*- coding: utf-8 -*-

  2. #****************************************************************

  3. # module1_case.py

  4. # Author : Vince

  5. # Version : 1.0

  6. # Date : 2011-3-10

  7. # Description: 内容服务系统测试代码

  8. #****************************************************************

  9. from testframe import *

  10. STRUCTCOL=14 #结构体开始列

  11. #content_book_list用例集

  12. def content_book_list_suite():

  13. print 'Test Begin,please waiting...'

  14. suiteid=4 #设置测试输入Sheetindex=1

  15. excelobj.del_testrecord(suiteid) #清除历史测试数据

  16. casecount=excelobj.get_ncase(suiteid)

  17. checklist=['resultCode', 'listid', 'name', 'total', 'from', 'size']

  18. structitem=['bookid', 'name', 'title', 'progress', 'words', 'authorid', 'authorName']

  19. #获取Case基本信息

  20. caseinfolist=get_caseinfo(excelobj, suiteid)

  21. for caseid in range(0, casecount):

  22. #检查是否执行该Case

  23. if excelobj.read_data(suiteid,excelobj.casebegin+caseid, 2)=='N':

  24. write_result(excelobj, suiteid, caseid, excelobj.resultcol, 'NT')

  25. continue #当前Case结束,继续执行下一个Case

  26. #获取测试数据

  27. sInput=get_input(excelobj, suiteid, caseid, caseinfolist)

  28. XmlString=run_case(com_ipport, sInput) #执行调用

  29. dict=get_xmlfile_dict(os.getcwd()+'\doc\Test.xml')

  30. itemdict=dict['root']

  31. ret1=check_item(excelobj, suiteid, caseid,itemdict, checklist, excelobj.argbegin+excelobj.argcount)

  32. structdict=dict['root']['books']['book']

  33. ret2=check_struct_item(excelobj, suiteid, caseid,structdict, structitem, STRUCTCOL, 2)

  34. write_result(excelobj, suiteid, caseid, excelobj.resultcol, ret1, ret2)

  35. print 'Test End!'

3、测试入口

 
  1. # -*- coding: utf-8 -*-

  2. #****************************************************************

  3. # main.py

  4. # Author : Vince

  5. # Version : 1.0

  6. # Date : 2011-3-16

  7. # Description: 测试组装,用例执行入口

  8. #****************************************************************

  9. from testframe import *

  10. from xxx_server_case import *

  11. from xxx_server_case import *

  12. import xxx_server_case

  13. #服务系统接口测试

  14. #设置测试环境

  15. xxx_server_case.excelobj=create_excel(os.getcwd()+'\doc\TestDemo_testcase.xls')

  16. xxx_server_case.com_ipport='XXX.16.1.35:8080'

  17. begin =datetime.datetime.now()

  18. #Add testsuite begin

  19. content_book_list_suite()

  20. #Add other suite from here

  21. #Add testsuite end

  22. #profile.run("content_book_list_suite()")

  23. print 'Total Time:', datetime.datetime.now()-begin

  24. statisticresult(xxx_server_case.excelobj)

  25. xxx_server_case.excelobj.close()

总结:

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

          视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1536518.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

美团2023年财报:全年营收2767亿元,即时配送订单219亿笔

3月22日,美团(股票代码:3690.HK)发布2023年第四季度及全年业绩。公司各项业务继续取得稳健增长,全年营收2767亿元(人民币,下同),同比增长26%,经营利润134亿元。 本年度,美团继续围绕“零售科技”战略&…

C# winform修改背景图 控件双向绑定 拖拽打开图片

修改背景图 说明 这里我准备基于百度飞桨PaddleSeg项目的人像分割模块做一个人像抠图,这里顺便用上了双向绑定和图片拖拽打开。 下面就是示例: 用颜色替换 用背景图替换 保存成功后的图片 一、使用百度飞桨PaddleSeg //初始化 引擎engine new Padd…

用易货模式做成小程序商城的可行性分析

在当前的商业环境中,易货模式已经逐渐被人们所熟知和接受。这种以物易物的交易方式,不仅能帮助企业降低库存压力,还能有效促进资源的优化配置。那么,如果我们用易货模式来打造一个小程序商城,这样的想法是否可行呢&…

【探索Linux】—— 强大的命令行工具 P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)

阅读导航 引言一、TCP协议二、TCP网络程序模拟实现1. 预备代码⭕ThreadPool.hpp(线程池)⭕makefile文件⭕打印日志文件⭕将当前进程转变为守护进程 2. TCP 服务器端实现(TcpServer.hpp)3. TCP 客户端实现(main函数&…

Doris实战——天眼查Doris实时数仓构建

目录 前言 一、业务背景 二、原有架构及痛点 三、理想架构 四、技术选型 五、新数仓架构 六、应用场景优化 6.1 人群圈选 6.2 C端分析数据及精准营销线索场景 七、优化经验 八、规模和收益 九、未来规划 原文大佬的这篇实时数仓构建有借鉴意义的,这些摘…

JavaEE 初阶篇-深入了解进程与线程(常见的面试题:进程与线程的区别)

🔥博客主页: 【小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录 1.0 进程概述 2.0 线程概述 2.1 多线程概述 3.0 常见的面试题:谈谈进程与线程的区别 4.0 Java 实现多线程的常见方法 4.1 实现多线程方法 - 继承 Thread 类…

从先序与中序遍历序列构造二叉树

从先序与中序遍历序列构造二叉树 描述: 给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。 递归法 解题思路: 通过先序遍历我…

山西生态与智慧水利科技展会之华锐VRAR元宇宙水利应用展位恭候大驾

2024黄河流域生态与智慧水利科技博览会于今日在山西太原隆重开启,本次展会持续至3月24日共三天时间,相比之前的水展,这次展会区域之广辐射了中部六省,展会内容之多包括黄河流域生态保护的现状与挑战、水资源管理与水污染防治、智慧…

鸿蒙开发-UI-动画-页面间动画

鸿蒙开发-UI-组件导航-Navigation 鸿蒙开发-UI-组件导航-Tabs 鸿蒙开发-UI-图形-图片 鸿蒙开发-UI-图形-绘制几何图形 鸿蒙开发-UI-图形-绘制自定义图形 鸿蒙开发-UI-图形-页面内动画 鸿蒙开发-UI-图形-组件内转场动画 鸿蒙开发-UI-图形-弹簧曲线动画 文章目录 前言 一、放大缩…

今天简单聊聊容器化

什么是容器化 容器化(Containerization)是一种软件开发和部署的方法,其核心思想是将应用程序及其所有依赖项打包到一个独立的运行环境中,这个环境被称为容器。容器化技术使得应用程序可以在不同的计算环境中以一致的方式运行&…

ES集群不识别节点SSL证书的问题处理

问题描述 在启动ES服务并试图加入其他节点上已启动的集群时,出现报错(原文是一大段话,我按语义拆成了几段): [2024-03-19T16:32:02,844][WARN ][o.e.c.s.DiagnosticTrustManager] [node-2-master] failed to establish trust with server a…

宏璇物流邀您参观2024快递物流供应链与技术装备展览会

展会介绍 ESYE CHINA 2024快递物流展是亚洲范围内超大规模的快递物流业展示平台,由于展会的需求及扩大市场的影响力,ESYE 2024转战杭州,凭借着先进的经济发展水平、优越的地理位置、成熟的产业环境以及巨大的电商、微商、零售业、消费的市场…

云手机为电商提供五大出海优势

出海电商行业中,各大电商平台的账号安全是每一个电商运营者的重中之重,账号安全是第一生产力,也是店铺运营的基础。因此多平台多账号的防关联管理工具成了所有电商大卖家的必备工具。云手机最核心的优势就是账户安全体系,本文将对…

ky10.aarch64安装Jenkins

参考地址:《安装部署 Jenkins》 前言 有war包和rpm两种安装方式,如果是长期使用更加推荐rpm的安装方式,可以更好的管理Jenkins; 我此次安装jenkins主要用于测试和简单的个人使用,所以选择更轻便的war安装。 1 下载J…

【C#】C#窗体应用修改窗体的标题和图标

修改窗体顶部的标题和图表,如果不修改则会使用默认的图标,标题默认为Form1,如第一张图,这时候如果想换成和系统有关的内容,如第二张图,可以使用下面的方法进行修改,修改后打开该软件任务栏显示的…

【yaml包如何安装】

【yaml包如何安装】 安装yaml包(也称为pyyaml) ,可以直接使用如下述命令 pip install pyyaml

LeetCode - 存在重复元素

219. 存在重复元素 II 这道题可以用两个方法解决。 哈希表 从左到右遍历数组,并将数组的下标存到hash中,在遍历数字的过程中,如果hash中不存在nums[i],将nums[i]加入到hash当中,若存在,则判断下标之间的关…

【算法】数组-移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的…

Java的IO之BIO

Java IO流是用于处理输入和输出的机制,用于读取和写入数据。Java提供了丰富的IO类和接口,用于处理不同类型的数据和操作。Java中的IO模型主要分为BIO和NIO两种,他们可以分别被视为IO编程的不同风格或模式,并非IO流具体的类型&…

云计算2主从数据库

设置主从数据库的目的是将数据库1和数据库2分别建在两个虚拟机上,并实现数据互通访问 首先准备两个虚拟机,这里示例ip分别为: 192.168.200.10;192.168.200.20 修改主机名,一个是mysql1,一个是mysql2&#x…