本文将介绍实现软件自动化测试其中一种测试方法------Windows API技术,当然,此种方法仅限于对.Net软件使用,还有一些比较通用的方法,将在后续的文章中进行介绍(使用UIAutomation类).
反射技术具体是什么原理,本文将不做任何介绍,大家可以去网上搜索一下,有很多这方面的文章介绍,本文只介绍如何使用Windows API技术进行.NET软件的UI界面的自动化测试.
废话少说,多做实事!(本文所有代码均在VS2008环境下测试通过)
一. 创建待测试程序
1. 启动VS2008,建立一个C#的WinForm工程,并将工程文件名命名为AUT(Application Under Test)
2. 创建的Form上的按钮布局,如下图所示
3. 一个菜单(包含一个以及菜单File以及一个二级菜单Exit),一个TextBox,一个ComboBox,一个Button以及一个ListBox
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.ListBox listBox1;
4. 给Button按钮添加一个消息响应函数
private void button1_Click(object sender, EventArgs e)
{
string tb = textBox1.Text;
string cb = comboBox1.Text;
if (tb == cb)
{
listBox1.Items.Add("Result is a tie");
}
else if (tb == "paper" && cb == "rock" ||
tb == "rock" && cb == "scissors" ||
tb == "scissors" && cb == "paper")
{
listBox1.Items.Add("The TextBox wins");
}
else
{
listBox1.Items.Add("The ComboBox wins");
}
}
5. 给Exit菜单添加一个消息响应函数
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}
6.给ComboBox控件添加三个Item,分别为paper,rock,scissions
7. 编译待测试程序,生成文件名为AUT.exe的待测试程序
二. 创建测试程序
1. 启动VS2008,创建一个C#控制台程序,并命名为WindowsUITest
2. 在工程中添加以下using语句
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using System.Text;
3. 声明需要使用到的WIndows API
[DllImport("user32.dll", EntryPoint = "FindWindow", CharSet = CharSet.Auto)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowsName);
[DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]
static extern IntPtr FindWindowEx(IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow);
//用于发送WM_CHAR消息
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
static extern void SendMessage1(IntPtr hWnd, uint Msg, int wParam, int lParam);
//用于发送WM_COMMAND消息
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
static extern void SendMessage2(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam);
//用于发送WM_GETTEXT消息
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
static extern int SendMessage3(IntPtr hWndControl, uint Msg, int wParam, byte[] lParam);
//用于发送LB_FINDSTRING消息
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
static extern int SendMessage4(IntPtr hWnd, uint Msg, int wParam, byte[] lParam);
//用于发送WM_LBUTTONDOWN和WM_LBUTTONUP消息
[DllImport("user32.dll", EntryPoint="PostMessage", CharSet=CharSet.Auto)]
static extern bool PostMessage1(IntPtr hWnd, uint Msg, int wParam, int lParam);
//菜单函数相关
[DllImport("user32.dll")]
static extern IntPtr GetMenu(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetSubMenu(IntPtr hMenu, int nPos);
[DllImport("user32.dll")]
static extern int GetMenuItemID(IntPtr hMenu, int nPos);
4. 定义查询窗体的函数
static IntPtr FindTopLevelWindow(string caption, int delay, int maxTries)
{
IntPtr mwh = IntPtr.Zero;
bool formFount = false;
int attempts = 0;
do
{
mwh = FindWindow(null, caption);
if (mwh == IntPtr.Zero)
{
Console.WriteLine("Form not yet found");
Thread.Sleep(delay);
++attempts;
}
else
{
Console.WriteLine("Form has been found");
formFount = true;
}
}
while (!formFount && attempts < maxTries);
if (mwh != IntPtr.Zero)
{
return mwh;
}
else
{
throw new Exception("Could not found Main Window");
}
}
static IntPtr FindMainWindowHandle(string caption, int delay, int maxTries)
{
return FindTopLevelWindow(caption, delay, maxTries);
}
static IntPtr FindMessageBox(string caption)
{
int delay = 100;
int maxTries = 25;
return FindTopLevelWindow(caption, delay, maxTries);
}
static IntPtr FindWindowByIndex(IntPtr hwndParent, int index)
{
if (index == 0)
{
return hwndParent;
}
else
{
int ct = 0;
IntPtr result = IntPtr.Zero;
do
{
result = FindWindowEx(hwndParent, result, null, null);
if (result != IntPtr.Zero)
{
++ct;
}
}
while (ct < index && result != IntPtr.Zero);
return result;
}
}
5. 定义发送字符串/字符的函数
static void SendChar(IntPtr hControl, char c)
{
uint WM_CHAR = 0x0102;
SendMessage1(hControl, WM_CHAR, c, 0);
}
static void SendChars(IntPtr hControl, string s)
{
foreach (char c in s)
{
SendChar(hControl, c);
}
}
6. 定义点击鼠标按钮的函数
static void ClickOn(IntPtr hControl)
{
uint WM_LBUTTONDOWN = 0x0201;
uint WM_LBUTTONUP = 0x0202;
PostMessage1(hControl, WM_LBUTTONDOWN, 0, 0);
PostMessage1(hControl, WM_LBUTTONUP, 0, 0);
}
7. 编写测试代码
[STAThread]
static void Main(string[] args)
{
try
{
Console.WriteLine("\nLaunching application under test");
string path = Directory.GetCurrentDirectory() + "\\AUT.exe";
Process p = Process.Start(path);
Thread.Sleep(2000);
Console.WriteLine("\nFinding main window handle");
IntPtr mwh = FindMainWindowHandle("Form1", 100, 25);
Console.WriteLine("Main window handle = " + mwh);
Thread.Sleep(2000);
//因为测试程序是使用C#创建的,所以控件的Class名称会比较奇怪,可以使用SPY++工具来查看
IntPtr tb = FindWindowEx(mwh, IntPtr.Zero, "WindowsForms10.EDIT.app.0.378734a", "");
IntPtr cb = FindWindowEx(mwh, IntPtr.Zero, "WindowsForms10.COMBOBOX.app.0.378734a", "");
IntPtr bt = FindWindowEx(mwh, IntPtr.Zero, null, "button1");
IntPtr lb = FindWindowEx(mwh, IntPtr.Zero, "WindowsForms10.LISTBOX.app.0.378734a", "");
if (tb == IntPtr.Zero || cb == IntPtr.Zero ||
bt == IntPtr.Zero || lb == IntPtr.Zero)
{
throw new Exception("Unable to find all controls");
}
else
{
Console.WriteLine("All control handles found");
}
//设置数据
SendChars(tb, "paper");
SendChars(cb, "rock");
ClickOn(bt);
Thread.Sleep(1000);
uint LB_FINDSTRING = 0x018F;
byte[] bResult = Encoding.ASCII.GetBytes("The TextBox wins");
int result = SendMessage4(lb, LB_FINDSTRING, -1, bResult);
//uint WM_GETTEXT = 0x000D;
if (result >= 0)
{
Console.WriteLine("\nTest scenario result = Pass");
}
else
{
Console.WriteLine("\nTest scenario result = Fail");
}
Console.WriteLine("\nExiting app in 3 seconds...");
Thread.Sleep(3000);
IntPtr hMainMenu = GetMenu(mwh);
//IntPtr hMainMenu = FindWindowEx(mwh, IntPtr.Zero, "WindowsForms10.Window.8.app.0.378734a", null);
IntPtr hFile = GetSubMenu(hMainMenu, 0);
int iexit = GetMenuItemID(hFile, 0);
uint WM_COMMAND = 0x0111;
SendMessage2(mwh, WM_COMMAND, iexit, IntPtr.Zero);
Console.WriteLine("\nDone");
Console.ReadLine();
}
catch (System.Exception ex)
{
}
}
8. 编译测试程序,OK,大功告成