Coded UI Test中的数据驱动测试
有关什么是Coded UI Test以及如何使用Coded UI Test可以查看我的另一篇文章:http://www.cnblogs.com/jaxu/p/3706652.html
本文主要介绍如何在Coded UI Test中使用数据驱动测试。考虑这样一个场景:开发人员提交了一个函数,该函数实现了一个数学公式的运算,通过接收两个数字并进行数学运算给出结果。测试人员根据给定的数学公式,需要考虑提供各种不同情况的值来循环测试该函数。显然,我们需要提供一个数据源,根据数据源中提供的不同的值来进行自动化测试。这是最常见的数据驱动测试的案例。在基于Coded UI Test的Webpage自动化测试中,浏览器兼容性问题是通常要考虑的,我们可以在数据源(数据源可能是一个记事本或者一个简单的Excel表格)中提供要测试的浏览器的名称和版本号,然后让Coded UI Test自动加载不同的浏览器来循环测试目标页面。下面的内容会介绍这些方法。
这里有两篇文章详细描述了如何通过[DataSource]特征属性来完成数据驱动Coded UI Test。其基本思想是通过在TestMethod前面添加[DataSource]特征属性,并指定数据源的类型和位置,然后Coded UI Test的测试方法在运行时会自动读取数据源中的数据,在迭代中完成比对。
http://blogs.msdn.com/b/mathew_aniyan/archive/2009/03/17/data-driving-coded-ui-tests.aspx
http://msdn.microsoft.com/en-us/library/ms182527.aspx
下面这段代码说明了这一情况:
[TestMethod]
[DataSource("System.Data.Odbc", @"Dsn=Excel Files;dbq=C:\Box Office Results.xlsx;defaultdir=C:;driverid=1046;maxbuffersize=2048;pagetimeout=5", "BoxOfficeResults$", DataAccessMethod.Sequential)]
public void MyTest()
{
int rowIndex = this.TestContext.DataRow.Table.Rows.IndexOf(this.TestContext.DataRow);
string s = this.TestContext.DataRow[].ToString();
}
特征属性[DataSource]有多个重载,以用来通过不同的方式指定数据源的位置。如果你不知道上述代码中的数据源连接字符串是如何提供的,可以在Visual Studio中尝试添加数据源操作,然后拷贝其中自动生成的数据源连接字符串。
- 在Visual Studio中通过VIEW->Other Windows->Data Sources打开数据源窗口
- 点击添加一个新的数据源,选择Database
- 点击New Connection...在打开的窗口中通过ODBC方式选择Excel文件,Visual Studio会自动为你生成连接字符串
- 拷贝该连接字符串
运行上面的代码,你会发现其实数据读取工作是在迭代中完成的。也就是说测试方法会被不断地迭代,直到数据源中所有行均被读取完成。数据迭代的方式可通过枚举DataAccessMethod来指定,一共两种方式:顺序读取或随机读取。
我们将上面代码中的Excel文件放到C盘根目录,然后调试代码。Excel中的第一行默认会被作为标题,数据默认会从第二行开始读取,所以第一次迭代的时候DataRow[0]返回的是Excel中A2单元格的内容。TestContext对象被作为数据源上下文,通过它你可以找到数据源的一些属性。例如通过上面代码中的第一行获取到数据源的行索引。这里是msdn上有关DataRow属性的说明http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.testcontext.datarow.aspx
将数据源连接字符串直接写在代码里并不是什么明智之举,那有没有什么方法可以将它移到配置文件中呢?答案是肯定的!通过msdn的这篇文章我们可以得知如何将数据源连接字符串移到配置文件中http://msdn.microsoft.com/en-us/library/ms243192.aspx
首先在工程中添加Application Configuration File,即App.config。
内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="microsoft.visualstudio.testtools" type="Microsoft.VisualStudio.TestTools.UnitTesting.TestConfigurationSection, Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</configSections> <connectionStrings>
<add name="MyExcelConn" providerName="System.Data.Odbc" connectionString="Dsn=Excel Files;dbq=C:\Box Office Results.xlsx;defaultdir=C:;driverid=1046;maxbuffersize=2048;pagetimeout=5"/>
</connectionStrings>
<microsoft.visualstudio.testtools>
<dataSources>
<add name="BoxOfficeResults" connectionString="MyExcelConn" dataTableName="BoxOfficeResults$" dataAccessMethod="Sequential"/>
</dataSources>
</microsoft.visualstudio.testtools>
</configuration>
注意microsoft.visualstudio.testtools节中type属性的版本号可能会由于使用的.NET Framework版本的不同有所变化。2.0为8.0.0.0,3.5为9.0.0.0,3.5以上应该是10.0.0.0。在App.config文件中添加上述配置信息之后,将测试方法的[DataSource]特征属性改成
[DataSource("BoxOfficeResults")]
如果Excel文件中存在多个表,则可以在配置文件的<dataSources>中添加多个<add>节点,指明不同的数据表名称和数据读取方式。
还记得文章一开始提到的使用数据驱动测试来实现多浏览器兼容性测试吗?我们可以通过下面的代码来实现。
BrowserWindow.CurrentBrowser = this.TestContext.DataRow[].ToString();
将不同版本的浏览器名称添加到数据源文件中,可以是一个简单的记事本或者.csv文件,如:
BrowserType
IE
firefox
chrome
注意第一行是标题,[DataSource]特征属性在读取数据时始终会将第一行默认为标题行,数据默认是从第二行开始读取的。浏览器只需要提供名称即可,大小写没有关系。如果你的测试方法需要在不同的浏览器中完成测试,则可以尝试该方法,但我不保证其中是否会涉及到兼容性问题,就像我在前一篇文章中提到的Coded UI Test如何在页面上搜索一个控件,如果搜索的条件存在浏览器兼容性问题,则可能会抛出异常。
虽然通过[DataSource]特征属性可以非常方便地读取数据源中的数据来完成数据驱动测试,但是有些情况下这种方式并不适用。考虑一个简单的需求:被测试页面上有一个表格,其数据来源于服务器上的一个Excel文件。通过编写基于数据驱动测试的Coded UI Test方法来检查页面上显示的内容是否与Excel文件一致。
由于[DataSource]特征属性是以迭代的方式来进行数据驱动测试的,因此我们无法在数据迭代的过程中去遍历页面UI元素。况且,如果迭代中存在Assert断言,也不太方便我们输出测试结果。最终的期望是,遍历整个UI Table,通过与预先读取的数据源中的数据进行逐一比对来完成整个测试,其过程可能是这样:
- 读取数据源中的数据并缓存。由于无法使用[DataSource]特征属性自动读取数据,因此不得不自己编写代码来完成数据读取操作。
- 找到UI Table进行遍历,将所有单元格的数据缓存。
- 将两部分缓存的数据进行比对,这可能需要预先提供Mapping以帮助完成数据比对过程。
我们以http://www.cnblogs.com/jaxu/p/3635634.html页面中的表格为例来看看如何实现这一过程。
下面是UIMap2.uitest中的完整代码:
public partial class UIMap2
{
public void LaunchPage()
{
this.UIExcelInteractiveViewWindow.LaunchUrl(new Uri("http://www.cnblogs.com/jaxu/p/3635634.html"));
} public void TestTableData()
{
HtmlTable targetTable = this.UIExcelInteractiveViewWindow.UIExcelInteractiveViewDocument.UICnblogs_post_bodyPane.UIItemTable;
HtmlRow rowall = new HtmlRow(targetTable);
UITestControlCollection rows = rowall.FindMatchingControls();
Dictionary<int, Dictionary<int, string>> PageTableDataCache = new Dictionary<int, Dictionary<int, string>>();
int rowCount = rows.Count; for (int i = ; i < rowCount; i++)
{
HtmlCell allTD = new HtmlCell(rows[i]);
UITestControlCollection TDs = allTD.FindMatchingControls();
Dictionary<int, string> cellsDictionary = new Dictionary<int, string>(); int tdCount = TDs.Count;
for (int j = ; j < tdCount; j++)
{
cellsDictionary.Add(j, ((HtmlCell)TDs[j]).InnerText);
} PageTableDataCache.Add(i, cellsDictionary);
} Dictionary<int, Dictionary<int, string>> ExcelDataCache = GetExcelData(ConfigurationManager.AppSettings["ExcelPath"], "BoxOfficeResults");
// load mapping
MappingRow[] mappingRows = GetMappingRowCollection(); string msg = string.Empty; for (int i = ; i < mappingRows.Length; i++)
{
Dictionary<int, string> pageRowCellsDictionary = PageTableDataCache[mappingRows[i].PageTableTrIndex];
Dictionary<int, string> excelRowCellsDictionary = ExcelDataCache[mappingRows[i].ExcelRowNum];
MappingColumn[] mappingColumns = mappingRows[i].MappingColumn;
for (int j = ; j < mappingColumns.Length; j++)
{
string excelValue = excelRowCellsDictionary[mappingColumns[j].ExcelColumnNum];
string pageValue = pageRowCellsDictionary[mappingColumns[j].PageTableTdIndex]; Assert.AreEqual(excelValue, pageValue, string.Format("Validation failed at row {0} column {1}", mappingRows[i].ExcelRowNum, mappingColumns[j].ExcelColumnNum));
}
}
} private Dictionary<int, Dictionary<int, string>> GetExcelData(string filePath, string sheetName)
{
Dictionary<int, Dictionary<int, string>> sheetDataDic = new Dictionary<int, Dictionary<int, string>>();
Application excel = new Application();
excel.Visible = false;
excel.UserControl = true; try
{
Workbook wb = (Workbook)excel.Application.Workbooks.Open(filePath,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value,
Missing.Value
); foreach (var worksheet in wb.Worksheets)
{
Worksheet ws = ((Worksheet)worksheet);
if (ws.Name.Equals(sheetName))
{
int rowscount = ws.UsedRange.Cells.Rows.Count;
int colscount = ws.UsedRange.Cells.Columns.Count; for (int i = ; i < rowscount; i++)
{
Dictionary<int, string> cellsDictionary = new Dictionary<int, string>();
for (int j = ; j < colscount; j++)
{
Range range = ws.Cells.get_Range(ConvertNumberToName(j) + (i + ));
string cellText = ((object)range.Text) == null ? "" : ((object)range.Text).ToString();
cellsDictionary.Add(j, cellText);
}
sheetDataDic.Add(i, cellsDictionary);
}
break;
}
}
}
finally
{
excel.Application.Workbooks.Close();
excel.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
excel = null;
GC.Collect();
} return sheetDataDic;
} private string ConvertNumberToName(int index)
{
if (index < ) { throw new Exception("invalid parameter"); } List<string> chars = new List<string>();
do
{
if (chars.Count > ) index--;
chars.Insert(, ((char)(index % + (int)'A')).ToString());
index = (int)((index - index % ) / );
} while (index > ); return String.Join(string.Empty, chars.ToArray());
} private MappingRow[] GetMappingRowCollection()
{
Mapping mapping = null;
MappingRow[] mappingRowCollection = null; XmlSerializer serializer = new XmlSerializer(typeof(Mapping)); using (FileStream fs = new FileStream(ConfigurationManager.AppSettings["MappingsPath"], FileMode.Open))
{
using (XmlReader reader = XmlReader.Create(fs))
{
mapping = (Mapping)serializer.Deserialize(reader);
}
} if (mapping != null)
{
mappingRowCollection = mapping.MappingRow;
} return mappingRowCollection;
}
}
其中有两个public方法:LaunchPage()用来导航到目标页面;TestTableData()用来遍历目标页面上的表格并与数据源Excel中的数据进行比对,给出测试结果。
私有方法GetExcelData()用来读取Excel文件中的数据,其中用到了Microsoft.Office.Interop.Excel程序集中的对象,需要在工程中单独添加引用。数据按行和列的方式存放到Dicitionary字典对象中,字典中的Key为每一行的行号,Value则是另一个字典,包含该行所有的列。事实上,程序中的其它地方也用到了这种数据存储结构。
在TestTableData()方法中,一共完成了三个步骤:
- 首先遍历页面上的Table,将单元格的内容缓存到字典对象PageTableDataCache中。该字典对象的数据存储结构与上面讲到的Excel数据存储结构相同。值得注意的是,在遍历Table的过程中由于是遍历Table下面所有HtmlRow的集合,此处不包含Header部分,因此Table的表头部分的数据不会被存储到字典对象中。
- 通过GetExcelData()方法将Excel中的数据缓存到字典对象ExcelDataCache中。
- 添加一个Mapping,用来对页面上的表格和Excel进行映射。为什么需要Mapping?因为页面上表格中单元格的行和列与Excel中的行和列并不总是一一对应的,所以这里必须要通过Mapping来进行映射,以确定如何进行比对。Mapping可以是一个XML文件,在程序中通过反序列化的方式进行读取,该操作由私有方法GetMappingRowCollection()完成。下面是Mapping XML文件的内容,有关如何通过Visual Studio自动生成XML反序列化的类,可以查看这篇文章http://www.cnblogs.com/jaxu/p/3632077.html。
<?xml version="1.0" encoding="utf-8" ?>
<Mapping>
<MappingRow ExcelRowNum="1" PageTableTrIndex="0">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="2" PageTableTrIndex="1">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="3" PageTableTrIndex="2">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="4" PageTableTrIndex="3">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="5" PageTableTrIndex="4">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="6" PageTableTrIndex="5">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="7" PageTableTrIndex="6">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="8" PageTableTrIndex="7">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="9" PageTableTrIndex="8">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
<MappingRow ExcelRowNum="10" PageTableTrIndex="9">
<MappingColumn ExcelColumnNum="0" PageTableTdIndex="0"/>
<MappingColumn ExcelColumnNum="1" PageTableTdIndex="1"/>
<MappingColumn ExcelColumnNum="2" PageTableTdIndex="2"/>
<MappingColumn ExcelColumnNum="3" PageTableTdIndex="3"/>
<MappingColumn ExcelColumnNum="4" PageTableTdIndex="4"/>
</MappingRow>
</Mapping>同时,由于程序中需要读取Excel文件以及反序列化Mapping XML文件,需要在App.config文件中添加两个配置项。
<appSettings>
<add key="MappingsPath" value="..\..\..\CodedUITestProject2\Mapping.xml"/>
<add key="ExcelPath" value="c:\Box Office Results.xlsx"/>
</appSettings>注意MappingPath的路径使用的是相对路径,通过将该文件设置为拷贝到输出路径以方便部署。方法是在Visual Studio的Solution Explorer中右键选择该文件->Properties,将Copy to Output Directory改成Copy always。
- 遍历Mapping中所有行和列,并比较PageTable和Excel中的数据,通过Assert断言来获取测试结果。由于Assert断言在测试失败时总会抛出异常而终止余下的测试步骤,可以参考文章http://www.cnblogs.com/jaxu/p/3706652.html中有关Assert断言部分对上述代码进行改进。
- 添加测试方法以调用LaunchPage()和TestTableData()
[TestMethod]
public void MyTest()
{
UIMap2 uimap = new UIMap2();
uimap.LaunchPage();
uimap.TestTableData();
}
下面的代码由Visual Studio自动生成并做了少量修改(将byte类型改为int),用来将Mapping XML进行反序列化。
/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Mapping
{ private MappingRow[] mappingRowField; /// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("MappingRow")]
public MappingRow[] MappingRow
{
get
{
return this.mappingRowField;
}
set
{
this.mappingRowField = value;
}
}
} /// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class MappingRow
{ private MappingColumn[] mappingColumnField; private int excelRowNumField; private int pageTableTrIndexField; /// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("MappingColumn")]
public MappingColumn[] MappingColumn
{
get
{
return this.mappingColumnField;
}
set
{
this.mappingColumnField = value;
}
} /// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public int ExcelRowNum
{
get
{
return this.excelRowNumField;
}
set
{
this.excelRowNumField = value;
}
} /// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public int PageTableTrIndex
{
get
{
return this.pageTableTrIndexField;
}
set
{
this.pageTableTrIndexField = value;
}
}
} /// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class MappingColumn
{ private int excelColumnNumField; private int pageTableTdIndexField; /// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public int ExcelColumnNum
{
get
{
return this.excelColumnNumField;
}
set
{
this.excelColumnNumField = value;
}
} /// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public int PageTableTdIndex
{
get
{
return this.pageTableTdIndexField;
}
set
{
this.pageTableTdIndexField = value;
}
}
}
UIMap2.uitest中的代码实现了Excel数据与页面Table中的内容比对,并最终通过了测试。不过从严格意义上来讲,这种情况应该不属于数据驱动测试的范畴,数据驱动测试的意义在于通过给定的数据来测试程序已有的功能,而上述情况是通过数据源来比对UI中的内容。不过我们仍然可以从中了解到如何编写代码来读取数据源并对Webpage中的表格进行Coded UI test。
Coded UI Test中的数据驱动测试的更多相关文章
- excel文件的groovy脚本在SoapUI中进行数据驱动测试
SoapUI Pro具有从外部文件读取数据的功能,例如:excel,csv等.但SoapUI不提供从excel文件读取数据的功能.因此,为了从SoapUI中的excel文件中读取数据,我们需要在gro ...
- 如何使用Coded UI Test对Webpage进行自动化测试
在Visual Studio中,Coded UI Test已经不是什么新特性了,较早版本的Visual Studio中就已经有这个东东了.它主要用来帮助自动化测试工程师和开发人员确保程序在UI方面没有 ...
- Coded UI Test对Webpage进行自动化测试
如何使用Coded UI Test对Webpage进行自动化测试 在Visual Studio中,Coded UI Test已经不是什么新特性了,较早版本的Visual Studio中就已经有这个 ...
- C#中通过Coded UI Test Web Page初体验(图文并茂,去繁就简!亲测通过哦~)
今天首次按照网上的步骤进行Coded UI测试,终于测试通过了,我这次进行的自动化测试是:打开浏览器,输入www.baidu.com,然后输入lty,然后点击页面中第一条数据的左侧位置(为了能获取到T ...
- 如何快速掌握DDT数据驱动测试?
1.前言 (网盗概念^-^)相同的测试脚本使用不同的测试数据来执行,测试数据和测试行为完全分离, 这样的测试脚本设计模式称为数据驱动.(网盗结束)当我们测试某个网站的登录功能时,我们往往会使用不同的用 ...
- 什么是Coded UI
什么是Coded UI Coded UI Test是Visual Studio 2010对于Testing Project(测试工程)提供的关于UI自动化测试的框架,支持Win32,Web,WPF等U ...
- 自动化测试架构设计 &&自动化持续集成测试任务实战[线性测试、模块驱动测试、数据驱动测试、关键字驱动测试]
1 为什么设计自动化测试架构 1.1 企业现状分析 压力大:产品需求不明确,上线时间确定,压力山大. 混乱:未立项,开发时间已过半,前期无控制,后期无保障. 疲于应付:开发人员交付的文件质量差,测试跟 ...
- Coded UI
Coded UI Test是Visual Studio 2010对于Testing Project(测试工程)提供的关于UI自动化测试的框架,支持Win32,Web,WPF等UI的自动化测试,是一个非 ...
- 浅谈UI设计中妙用无穷的深色系背景
英文:medium 译者:优设网 - 陈子木 链接:http://www.uisdc.com/ui-benefits-of-dark-background# --------------------- ...
随机推荐
- LoadRunner ---手动关联与预关联
手动关联 如果脚本很长,那么我们想找到一个脚本中哪些地方是需要关联的并不是一件容易的事情.这时,我们可以通过脚本对比的方法找 ...
- 超链接的那些事(二): 属性href
a标签的属性之一 href 1. 定义 href 属性用于指定超链接目标的 URL. 2. 用法 ①. 锚点 同一页面添加锚点 (1)<a href="#test"& ...
- MySQL安装常见问题(找不到文件,系统服务无法启动...)
在安装mysql时总是会遇到问题,每次重新安装都会花很多时间来排查.在网上其实有很多相关的文章,但很多都只讲了方法,但没讲具体细节问题,导致无法解决问题.其实有时候知道问题的原因,但总是因为一些细节问 ...
- background-orgin属性
重点内容是:背景的显示范围是在元素的内边距之内的,如果要想改变显示范围,可以使用background-orgin和background-clip进行调整. (1)background-orgin有三个 ...
- zzulioj 1907小火山的宝藏交易(dfs记忆化搜索)
#include <stdio.h> #include <algorithm> #include <string.h> #include <vector> ...
- canvas 画六边形边框
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 电梯多媒体WinForm项目Q&A总结
最近,我给一家公司做了个电梯多媒体软件,该软件使用C#编写,现在我将其中遇到的问题及其解决方法总结一下,以便下次再遇到同样的问题可以快速解决:同时,也给博友分享一下,共同学习,共同提高. 1.Ques ...
- throw er; Unhandled 'error' event Error: listen EADDRINUSE的解决方法
先把错误贴出来,如下: 出现此问题的原因是端口被占用,解决方法如下: 查看程序执行用到的端口的运行情况,本例是端口号3000; 占用该端口3000的进程是node,pid为244156,用kill命令 ...
- php数组函数
1.键值函数 array_values()返回数组元素值,组成一个新的索引数组 2.array_keys()返回数组所有键名,组成一个索引数组 3.in_array()检查数组中是否存在某个值 4.a ...
- 使用JMeter进行负载测试——终极指南
这篇教程讨论的是JMeter,它是一款基于Java的.集合了几个应用程序.具有特定用途的负载和性能测试工具. 本篇主要涉及的内容: 解释一下JMeter的用途 JMeter的实现方式以及采用的技术 安 ...