日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > C# >内容正文

C#

C#借助API实现黑盒自动化测试工具的编写

發(fā)布時(shí)間:2025/3/8 C# 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#借助API实现黑盒自动化测试工具的编写 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

C#借助API實(shí)現(xiàn)黑盒自動(dòng)化測(cè)試工具的編寫

本文代碼下載(VS2010開(kāi)發(fā)):http://download.csdn.net/source/2796362

本文摘要:

1:一個(gè)簡(jiǎn)單的例子????

?? 1.1:EnumChildWindows介紹

?? 1.2:主要源碼

2:難點(diǎn):如何獲取指定的控件句柄

?? 2.1:使用SPY++

?? 2.2:獲取控件位置

?? 2.3:獲取控件ID

1:一個(gè)簡(jiǎn)單的例子??

???? 在日常編碼過(guò)程中,我們常常會(huì)進(jìn)行自動(dòng)化測(cè)試。這里的自動(dòng)化測(cè)試不是指單元測(cè)試,而是模擬人工輸入來(lái)進(jìn)行快速的、高并發(fā)的測(cè)試。可以使用的自動(dòng)化工具有LOADRUNNER,以及目前在VS2010中的功能很強(qiáng)大的測(cè)試工作平臺(tái)(錄制操作步驟,自動(dòng)生成代碼)。但是,這些工具的熟練掌握也有一定的時(shí)間成本,并且,最主要的,對(duì)于一個(gè)程序員來(lái)說(shuō),那不夠靈活。所以,比較高效的一個(gè)做法是,調(diào)用WINDOWS API,自己動(dòng)手寫編碼來(lái)實(shí)現(xiàn)。

???? 下面做一個(gè)簡(jiǎn)單的演示。為了簡(jiǎn)便起見(jiàn),假設(shè)存在這樣一個(gè)應(yīng)用程序:

1:提供一個(gè)WINFORM窗體,上面存在一個(gè)TextBox,以及一個(gè)Button;

2:點(diǎn)擊Button,會(huì)彈出提示框,提示框內(nèi)容為TextBox的值;

???? 現(xiàn)在,測(cè)試要求如下:

1:在300臺(tái)機(jī)器上運(yùn)行上面的程序;

2:到這300臺(tái)機(jī)器上去點(diǎn)擊這個(gè)Button,看看上文中的功能2有沒(méi)有實(shí)現(xiàn);

???? 很顯然,實(shí)際情況中沒(méi)有這么簡(jiǎn)單的程序,實(shí)際的情況有可能是點(diǎn)擊Button,統(tǒng)一下載一個(gè)文件,而測(cè)試的要求可能就變?yōu)榭己朔?wù)器的負(fù)載?,F(xiàn)在,測(cè)試部顯然也沒(méi)有300個(gè)人坐在客戶機(jī)上驗(yàn)證測(cè)試的結(jié)果,這個(gè)時(shí)候,就需要我們提供一個(gè)自動(dòng)化的測(cè)試工具,來(lái)完成必要的測(cè)試任務(wù)。

???? 測(cè)試工具,首先也是一個(gè)C#的程序,它的主要目的是:

1:獲取上文應(yīng)用程序的窗口句柄,繼而獲取TextBox句柄及Button句柄;

2:為TextBox隨機(jī)填入一些字符;

3:模擬點(diǎn)擊Button;

1.1:EnumChildWindows介紹

?? 在這里需要介紹下EnumChildWindows,

EnumChildWindows可是個(gè)好東西,可以枚舉一個(gè)父窗口的所有子窗口:

BOOL EnumChildWindows(?
? HWND?hWndParent,???????? // handle to parent window // 父窗口句柄?
? WNDENUMPROC?lpEnumFunc,? // callback function // 回調(diào)函數(shù)的地址?
? LPARAM?lParam??????????? // application-defined value // 你自已定義的參數(shù)?
);

??? 就這么簡(jiǎn)單,讓我們?cè)俣x一個(gè)回調(diào)函數(shù),像下面這樣:

BOOL CALLBACK EnumChildProc(?
? HWND?hwnd,????? // handle to child window?
? LPARAM?lParam?? // application-defined value?
);

??? 在調(diào)用EnumChildWindows 這個(gè)函數(shù)時(shí),直到調(diào)用到最個(gè)一個(gè)子窗口被枚舉或回調(diào)函數(shù)返回一個(gè)false,否則將一直枚舉下去。

1.2:簡(jiǎn)單例子的主要源碼

??? 測(cè)試工具的主要代碼如下:

private void button1_Click(object sender, EventArgs e){//獲取測(cè)試程序的窗體句柄IntPtr mainWnd = FindWindow(null, "FormLogin");List<IntPtr> listWnd = new List<IntPtr>();//獲取窗體上OK按鈕的句柄 IntPtr hwnd_button = FindWindowEx(mainWnd, new IntPtr(0), null, "OK");//獲取窗體上所有控件的句柄EnumChildWindows(mainWnd, new CallBack(delegate(IntPtr hwnd, int lParam){listWnd.Add(hwnd);return true;}), 0);foreach (IntPtr item in listWnd){if (item != hwnd_button){char[] UserChar = "luminji".ToCharArray();foreach (char ch in UserChar){SendChar(item, ch, 100);}}}SendMessage(hwnd_button, WM_CLICK, mainWnd, "0");}public void SendChar(IntPtr hand, char ch, int SleepTime){PostMessage(hand, WM_CHAR, ch, 0);System.Threading.Thread.Sleep(SleepTime);}public static int WM_CHAR = 0x102;public static int WM_CLICK = 0x00F5;[DllImport("User32.dll", EntryPoint = "SendMessage")]public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam); [DllImport("user32.dll")]public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,string lpszClass, string lpszWindow);[DllImport("user32.dll", SetLastError = true)]public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);[DllImport("user32.dll")]public static extern int AnyPopup();[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);[DllImport("user32.dll")]public static extern int EnumThreadWindows(IntPtr dwThreadId, CallBack lpfn, int lParam);[DllImport("user32.dll")]public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam);[DllImport("user32.dll", CharSet = CharSet.Ansi)]public static extern IntPtr PostMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);[DllImport("user32.dll", CharSet = CharSet.Ansi)]public static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);[DllImport("user32.dll", CharSet = CharSet.Unicode)]public static extern IntPtr SendMessageA(IntPtr hwnd, int wMsg, int wParam, int lParam);[DllImport("user32.dll", CharSet = CharSet.Auto)]static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]public static extern int GetWindowTextLength(IntPtr hWnd);[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]public static extern IntPtr GetParent(IntPtr hWnd);public delegate bool CallBack(IntPtr hwnd, int lParam);

運(yùn)行效果:

2:難點(diǎn):如何獲取指定的控件句柄

?? 細(xì)心的人可能已經(jīng)發(fā)現(xiàn),上文中,給文本框賦值的地方,使用了如下代碼:

foreach (IntPtr item in listWnd){if (item != hwnd_button){char[] UserChar = "luminji".ToCharArray();foreach (char ch in UserChar){SendChar(item, ch, 100);}}}

?? 假設(shè)我們的窗體上有多個(gè)文本框,那么事實(shí)上,這段代碼會(huì)給所有的文本框輸入"luminji”字樣。這在多數(shù)應(yīng)用程序中都是不允許的,我們需要精確定位需要控制的控件。

?? 我們?cè)诘玫絆K按鈕的句柄的時(shí)候,使用了函數(shù):

IntPtr hwnd_button = FindWindowEx(mainWnd, new IntPtr(0), null, "OK");

?? 而想要獲取文本框句柄的時(shí)候,這個(gè)函數(shù)卻不能使用,因?yàn)?#xff0c;所有文本框都是沒(méi)有標(biāo)題的,也就是類似"OK"這個(gè)值。有人說(shuō),那就使用控件ID吧。且看:

2.1:獲取控件ID

?? 非.NET程序,一旦程序被生成,控件ID就是固定的,所以這一招,用在非.NET程序中,那是再好也不過(guò)了。

???

?? 根據(jù)ID來(lái)得到控件句柄的函數(shù)聲明如下:

[DllImport("user32.dll ", EntryPoint = "GetDlgItem")]public static extern IntPtr GetDlgItem( IntPtr hParent, int nIDParentItem);

?? 其中,第一個(gè)參數(shù)就是窗體的句柄,第二個(gè)參數(shù)就是控件ID。

?? 但是,顯然,這種方法不適用于我們的.NET程序,因?yàn)槲覀儠?huì)發(fā)現(xiàn),我們的.NET程序沒(méi)運(yùn)行一次,這個(gè)ID是變化的。

2.2:獲取控件位置

?? 所以,最終的一個(gè)方案是:根據(jù)控件位置,人工比對(duì)后得到我們想要的控件句柄。該函數(shù)的聲明如下:

? ?好了,現(xiàn)在的關(guān)鍵就是怎么取得這個(gè)控件的位置。我們?cè)赩S中查看,某個(gè)控件有X坐標(biāo)和Y坐標(biāo),以上面程序的這個(gè)TextBox來(lái)說(shuō),其在VS中顯示的位置是“70,83”,但是而VS中顯示的,是不包含標(biāo)題和邊框的坐標(biāo)值。但是這個(gè)坐標(biāo)值可以作為我們?nèi)斯け葘?duì)的參考。?

? ?更精確的坐標(biāo)值,我們寫代碼來(lái)實(shí)現(xiàn),如下:

EnumChildWindows(mainWnd, new CallBack(delegate(IntPtr hwnd, int lParam){listWnd.Add(hwnd);StringBuilder className = new StringBuilder(126);StringBuilder title = new StringBuilder(200);GetWindowText(hwnd, title, 200);RECT clientRect;GetClientRect(hwnd, out clientRect);int controlWidth = clientRect.Width;int controlHeight = clientRect.Height;int x = 0, y = 0;IntPtr parerntHandle = GetParent(hwnd);if (parerntHandle != IntPtr.Zero){GetWindowRect(hwnd, out clientRect);RECT rect;GetWindowRect(parerntHandle, out rect);x = clientRect.X - rect.X;y = clientRect.Y - rect.Y;Debug.Print(x.ToString());Debug.Print(y.ToString());}return true;}), 0); 注意,上面代碼中的X和Y就是某個(gè)控件的精確的X和Y值,記錄下來(lái),比對(duì)一下,我們就能得到精確的坐標(biāo)值了。在上文的例子中,我們的文本框的坐標(biāo)最終得到為“78,113”。 有了這個(gè)坐標(biāo)值,我們便知道這個(gè)控件的句柄,也就是hwnd是屬于哪個(gè)控件的了。

2.3:根據(jù)EnumChildWindows枚舉次序得到句柄

如果你不想這么麻煩,還有一種簡(jiǎn)單的方案,那就是利用EnumChildWindows的枚舉順序。要知道,在不同的機(jī)器上,EnumChildWindows枚舉一個(gè)窗體上子控件的順序是相同的,也就是說(shuō),如果有兩個(gè)文本框,它們?cè)谶@臺(tái)機(jī)器上被枚舉的順序一個(gè)是2,一個(gè)是3,那么,它們?cè)谄渌鼨C(jī)器上被枚舉的順序,也是這個(gè)固定次序。通過(guò)比對(duì),我們也能得到它們各自的句柄。當(dāng)然,如果我們有了這些句柄,還有什么是不能做到的呢?

2.4:使用SPY++

??? SPY++是微軟的一個(gè)工具,用戶獲取窗體上的ID或者類型或者句柄等信息。因?yàn)樵谖覀兊倪@個(gè)例子里,ID和句柄在每臺(tái)機(jī)器上都是不變的,所以這個(gè)工具對(duì)于我們來(lái)說(shuō),沒(méi)有多大的用處。但是,當(dāng)你HACK別人的程序的時(shí)候,它會(huì)發(fā)揮一定作用。

????

參考:

1:http://book.21www.cn/info/vb/api/4076.html

2:http://dev.firnow.com/course/3_program/cshapo/csharpjs/20100714/441439.html

總結(jié)

以上是生活随笔為你收集整理的C#借助API实现黑盒自动化测试工具的编写的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。