【机器人系列】支付宝支付控件输入框模拟输入,输入框模拟输入
http://www.bkjia.com/webzh/974312.html
企業中做自動化測試,會用到Selenium,它確實是一個強大,免費,而便捷的自動化測試框架。但有時候我們會遇到一些特別的瀏覽器輸入控件,他們不是正常的Html input元素,因此Selenium無法獲取到。這時候我們需要用本地化操作進行模擬。當然,這種技術也不僅可以用來做測試,還可以....你懂的。切入正題。
問題/任務描述: 實現向支付寶密碼控件自動輸入密碼
解決方案:使用JNA提供的Native代理,調用Windows系統函數,通過SendMessage函數發送字符,實現輸入。
具體步驟:(1)找到控件窗口的句柄(可以使用Spy++),總結如何找到的,這是一個自定義的算法.因為支付寶的控件類名是隨機的亂碼.
??(2)循環發送密碼的每一個字符。
上代碼:
?
package org.tbworks.auto_alipay;import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit;import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; import com.sun.jna.win32.W32APIOptions;/*** @author tangb Note: className means the class name of window (frame), you can* get it by spy++*/ public class AlipayEditInputTyper {// load the user32.dllfinal protected static User324J user32 = (User324J) Native.loadLibrary("user32", User324J.class, W32APIOptions.DEFAULT_OPTIONS);/*** @author tangb* You can not modify a final variable in the anonymous class, but you can modify its member if the member was not final.*/private static final class finalHandleContainer{private HWND handle;}// -----------------------privates-------------------------------/*** get browser frame's window handle by class name and window title* * @param browserClassName* : browser window's class name, you can get it by spy++* @param browserTitle* @return*/private static HWND getAlipayWindowHandle(String browserClassName,String browserTitle) {HWND deskTopHandle = user32.GetDesktopWindow();char[] winClass = new char[100];user32.GetClassName(deskTopHandle, winClass, 100);//System.out.println(winClass);if( deskTopHandle == null )throw new RuntimeException("Can not find the desktop's window handle!");return getSpecifiedWindowHandle(deskTopHandle,browserClassName,browserTitle);}/** search the children windows of the father window specified by father-handle and find the child window with specified className and substring of title* @param fatherHandle handle of the father window* @param className className of the child window* @param title title of the child window* @return*/private static HWND getSpecifiedWindowHandle(HWND fatherHandle, final String className,final String title){final finalHandleContainer alipayWindowHandleContainer = new finalHandleContainer();boolean result = user32.EnumChildWindows(fatherHandle, new WNDENUMPROC() {@Overridepublic boolean callback(HWND hWnd, Pointer data) {// TODO Auto-generated method stubchar[] winClass = new char[100];char[] winText = new char[200];user32.GetClassName(hWnd, winClass, 100);user32.GetWindowText(hWnd, winText, 200);System.out.println("Class Name:"+Native.toString(winClass)+" Title:"+Native.toString(winText));if(user32.IsWindowVisible(hWnd) && Native.toString(winClass).equals(className) && Native.toString(winText).contains(title)){alipayWindowHandleContainer.handle = hWnd;return false;//false means stop}return true; }}, Pointer.NULL);return alipayWindowHandleContainer.handle;}/** get all children's class-names and handles under specified father windows, and store them in a map.* @param fatherHandle the handle of father window* @return a map stores every class name and its window handles, e.g., key="CLASSONE", value="12312"*/private static Map<String,List<HWND>> getChildWindowClassHandleMap(HWND fatherHandle){final finalHandleContainer alipayWindowHandleContainer = new finalHandleContainer();final Map<String,List<HWND>> resultMap = new HashMap<String,List<HWND>>();boolean result = user32.EnumChildWindows(fatherHandle, new WNDENUMPROC() {@Overridepublic boolean callback(HWND hWnd, Pointer data) {// TODO Auto-generated method stubchar[] winClass = new char[100];user32.GetClassName(hWnd, winClass, 100); if(user32.IsWindowVisible(hWnd)){String tempClassName = Native.toString(winClass);if(resultMap.containsKey(tempClassName)){resultMap.get(tempClassName).add(hWnd);}else{List<HWND> tempHWNDList = new ArrayList<HWND>();tempHWNDList.add(hWnd);resultMap.put(tempClassName, tempHWNDList);}}return true; }}, Pointer.NULL);return resultMap;}/** In term of alipay password edit control, its class name changes very time. And I come up with one solution: the window class name with only one instance may be the alipay edit, others are not definitely. But times always change, so maybe this is not a permanently valid way! Cautions! * Get several potential alipay edit window. As sending messages to all the sub windows are not efficient, so it is necessary to shrink the range.* @param browserWindowHandle* @return*/private static List<HWND> getPotentialAlipayEditHandle(HWND browserWindowHandle) {Map<String,List<HWND>> candidates = getChildWindowClassHandleMap(browserWindowHandle);List<HWND> resultList = new ArrayList<HWND>();for(Iterator it =candidates.entrySet().iterator();it.hasNext();){Map.Entry<String, List<HWND>> next = (Map.Entry<String, List<HWND>>) it.next();List<HWND> tempList = next.getValue();if(tempList.size()==1){resultList.add(tempList.get(0));}}return resultList;}// -----------------------publics--------------------------------/*** Set password to all potential alipay password edit controls embedded in the browser page* * @param browserClassName* : browser's class name. e.g., MozillaWindowClass for firefox* @param browserTitleOrSubTitle* : browser frame's title, or you can just specified a sub-title* which can mark the alipay window uniquely. E.g., frame's title* is "123456-scs**0s90", you can just utilize "234" if it is ok.* @param className* : alipay edit frame's class name.* @param title* : alipay edit frame's title.* @param password* : the password.* @return* @throws InterruptedException */public static boolean setPassword(String browserClassName,String browserTitleOrSubTitle,String password) throws InterruptedException {HWND browserWindowHandle = getAlipayWindowHandle(browserClassName,browserTitleOrSubTitle);System.out.println("------------------------------------------------------------------------------------------------------");List<HWND> list = getPotentialAlipayEditHandle(browserWindowHandle);for (char c : password.toCharArray()) {TimeUnit.MILLISECONDS.sleep(500);for(HWND handle:list)user32.SendMessage(handle, MESSAGE.WM_CHAR.toInt(), (byte) c, 0);}return true;} }?
提一下JNA訪問的原理:JNA封裝了操作系統的dll,自然也包括Windows的User32.dll,JNA通過java動態代理,獲取你要的功能(傳入的接口),然后就可以在java中調用了。JNA的包,可以通過Maven獲取。
? ?以下是測試代碼:
?
package org.tbworks.auto_alipay;public class TestTyper {public static void main(String[] args) throws InterruptedException {// firefoxAlipayEditInputTyper.setPassword("MozillaWindowClass", "支付寶", "mypassword");// chromeAlipayEditInputTyper.setPassword("Chrome_WidgetWin_1", "支付寶", "mypassword");}}
這里測試了2個瀏覽器,僅僅實現了輸入功能。成功運行后,會看到字符鬼魅般的一個個輸入到密碼控件中。
?
?
源碼下載:https://github.com/tbwork/alipay_edit_typer.git
轉載于:https://www.cnblogs.com/davidwang456/articles/8665760.html
總結
以上是生活随笔為你收集整理的【机器人系列】支付宝支付控件输入框模拟输入,输入框模拟输入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用JNA解决自动化测试无法做密码输入操
- 下一篇: java.net.URISyntaxEx