多语言的自动化测试

时间:2021-09-25 06:01:31

有段时间team在做PC平台的一个产品的本地化工作,每天都会构建出很多的版本进行测试,而我们需要支持它的多语言自动化工作,首先我们team已经开发了一套测试系统,可以自动将build出来的软件部署到测试机上,然后运行指定的测试任务并回报结果,现在需要做的就是如何对测试机自动切换指定的语言。经过一番google和实验后,发现通过给intl.cpl传递指定的xml配置文件,即可实现语言的切换

1. 安装语言包

微软提供了MUI的安装包,或者通过windows update也可以获取到。

 

2. 准备对应语言的配置文件

配置文件的详细说明微软已经提供了:

https://msdn.microsoft.com/en-ie/goglobal/bb964650(en-us).aspx

附上我们使用的法语的配置文件内容以供参考:

 

 1 <gs:GlobalizationServices xmlns:gs="urn:longhornGlobalizationUnattend">
 2 
 3  <!-- user list -->
 4  <gs:UserList>
 5  <gs:User UserID="Current" CopySettingsToDefaultUserAcct="true" CopySettingsToSystemAcct="true"/>
 6  </gs:UserList>
 7 
 8  <!-- UI Language Prefernces -->
 9  <gs:MUILanguagePreferences>
10  <gs:MUILanguage Value="fr-FR"/>
11  </gs:MUILanguagePreferences>
12 
13  <!-- system locale -->
14  <gs:SystemLocale Name="fr-FR"/>
15 
16  <!-- user locale -->
17  <gs:UserLocale>
18   <gs:Locale Name="fr-FR" SetAsCurrent="true" ResetAllSettings="true"/>
19  </gs:UserLocale>
20 
21 </gs:GlobalizationServices>

 

3. 使用control.exe来配置系统的语言信息:

control.exe intl.cpl,,/f:""c:\languages\fr.xml""

之后重启即可。

 

为了更彻底的做到本地化,我们还将测试系统的用户名做了本地化的配置,也就是当要将系统语言切换到日文的时候,会同时创建一个日文的用户名,并在重启后自动登录,具体步骤如下:

1. 使用System.DirectoryServices.AccountManagement来编写代码实现创建系统账户的功能:

 1     public static class WindowsAccountsHelper
 2     {
 3         public static void CreateWindowsAccounts(string userName, string userPassword = "1wdv3rgn", string userGroup = "Administrators", int count = 1, string description = "Test account")
 4         {
 5             WindowsUser _user = new WindowsUser(userName, userPassword, description, userGroup, userName);
 6             _user.Create();
 7         }
 8 
 9         private class WindowsUser
10         {
11             private string name;
12             private string password;
13             private string description;
14             private string groupName;
15             private string displayName;
16             bool canChangePwd;
17             bool pwdExpires;
18 
19             public WindowsUser(string name, string password, string description, string groupName, string displayName, bool canChangePwd = false, bool pwdExpires = false)
20             {
21                 this.name = name;
22                 this.password = password;
23                 this.description = description;
24                 this.groupName = groupName;
25                 this.displayName = displayName;
26                 this.canChangePwd = canChangePwd;
27                 this.pwdExpires = pwdExpires;
28             }
29 
30             public bool Exist()
31             {
32                 using (PrincipalContext _context = new PrincipalContext(ContextType.Machine))
33                 {
34                     using (UserPrincipal _user = UserPrincipal.FindByIdentity(_context, name))
35                     {
36                         if (_user != null)
37                         {
38                             return true;
39                         }
40                     }
41                 }
42                 return false;
43             }
44 
45             public void Create()
46             {
47                 if (Exist())
48                     return;
49                 using (PrincipalContext _context = new PrincipalContext(ContextType.Machine))
50                 {
51                     using (UserPrincipal _user = new UserPrincipal(_context))
52                     {
53                         _user.SetPassword(password);
54                         _user.DisplayName = displayName;
55                         _user.Name = name;
56                         _user.Description = description;
57                         _user.UserCannotChangePassword = canChangePwd;
58                         _user.PasswordNeverExpires = pwdExpires;
59                         _user.Save();
60 
61                         using (GroupPrincipal _group = GroupPrincipal.FindByIdentity(_context, groupName))
62                         {
63                             _group.Members.Add(_user);
64                             _group.Save();
65                         }
66                     }
67                 }
68             }
69         }

 

2. 为了保证在重启后能够登录新创建的账号,需要修改下面的注册表信息:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]
"AutoAdminLogon"="1"
"ForceAutoLogon"="1"
"DefaultDomainName"=""
"DefaultUserName"="test account"
"DefaultPassword"="testaccount"

将“DefaultUserName”替换为在第一步中创建的用户名,并将这个reg文件导入到系统中。(注意,此文件需要保存为unicode格式)

 

以上都是对于测试环境的准备工作,在实际运行测试的过程中,由于很多控件是通过Name属性去定位的,故在多语言版本的build上会导致控件找不到,从而使测试失败。

为了支持多语言的控件查找,我们不得不维护一个xml文件,其中记录了同一个控件在不同语言的Name值:

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <Root>
 3   <WindowsUIConfig>
 4     <Item language ="JP">
 5       <SystemTrayName>RealPlayer Cloud</SystemTrayName>
 6       <OpenFileWindowFileNameTextName>ファイル名(N):</OpenFileWindowFileNameTextName>
 7       <SecurityWindowName>Windows セキュリティ</SecurityWindowName>
 8       <SecurityWindowUserName>ユーザー名</SecurityWindowUserName>
 9       <SecurityWindowPassword>パスワード</SecurityWindowPassword>
10       <VirtualFolderRightClickMenuName>メニュー</VirtualFolderRightClickMenuName>
11       <VirtualFolderViewMenuName>表示(V)</VirtualFolderViewMenuName>
12       <RunWindowName>ファイル名を指定して実行</RunWindowName>
13       <VirtualFolderDeleteConfirmWindowName>アイテムを消去</VirtualFolderDeleteConfirmWindowName>
14       <VirtualFolderRemoveConfirmWindowName>アイテムを削除</VirtualFolderRemoveConfirmWindowName>
15       <ControlPanelProgramsAndFeaturesWindowName1>プログラムと機能</ControlPanelProgramsAndFeaturesWindowName1>
16       <ControlPanelProgramsAndFeaturesWindowName2>プログラムと機能</ControlPanelProgramsAndFeaturesWindowName2>
17       <CollectionOpenMenuItem>開く(O)</CollectionOpenMenuItem>
18       <SaveAsWindowName>名前を付けて保存</SaveAsWindowName>
19       <Converter_OpenFileWindowName>ファイルを開く</Converter_OpenFileWindowName>
20       <CloudUploadWarning>Cloud Upload Warning</CloudUploadWarning>
21       <RefreshMenuItemName>最新の情報に更新(E)</RefreshMenuItemName>
22       <DownloaderIEToolsButtonName>ツール</DownloaderIEToolsButtonName>
23       <DownloaderIEManageAddOnsItemName>アドオンの管理(M)</DownloaderIEManageAddOnsItemName>
24       <DownloaderIEManageAddOnsDialogName>アドオンの管理</DownloaderIEManageAddOnsDialogName>
25       <DownloaderIEManageAddOnsEnableButtonName>有効にする(E)</DownloaderIEManageAddOnsEnableButtonName>
26       <DownloaderIEManageAddOnsDisableButtonName>無効にする(B)</DownloaderIEManageAddOnsDisableButtonName>
27       <DownloaderIEManageAddOnsCloseButtonName>閉じる(L)</DownloaderIEManageAddOnsCloseButtonName>
28       <DownloaderIEManageAddOnsToolBarPane>ツール バーと拡張機能(&amp;T)</DownloaderIEManageAddOnsToolBarPane>
29     </Item>
30   <AppUIConfig>
31     <Item language="JP">
32       <AddFilesToLibraryWindowName>ファイルの追加</AddFilesToLibraryWindowName>
33       <OpenFileWindowName>開く</OpenFileWindowName>
34       <OpenFileBrowserWindowName>ファイルを開く</OpenFileBrowserWindowName>
35       <OpenFileWindowFileNameTextName>開く(O):</OpenFileWindowFileNameTextName>
36       <ScanDiskAddMediaDialogName>メディアの追加</ScanDiskAddMediaDialogName>
37       <ScanDiskComboBoxName>メディアの検索(L):</ScanDiskComboBoxName>
38       <ScanDiskListName>メディアの検索(L):</ScanDiskListName>
39       <ScanDiskScanWindowName>メディア スキャン</ScanDiskScanWindowName>
40       <MyVideosFolderName>マイ ビデオ</MyVideosFolderName>
41       <MyDocumentsFolderName>マイ ドキュメント</MyDocumentsFolderName>
42       <AbnormalDialogName>Abnormal Shutdown</AbnormalDialogName>
43       <RealPlayerFileOpenWindowName>開く</RealPlayerFileOpenWindowName>
44       <SignOutWindowName>サイン アウトしますか?</SignOutWindowName>
45       <SignOutFinishWindowName>サイン アウト完了</SignOutFinishWindowName>
46     </Item>
47   </AppUIConfig>
48 </Root>

上面的xml就是一个针对日语的配置文件,分为WindowsUIConfig和AppUIConfig,其中前者是对系统控件的多语言Name属性的记录,会随着系统语言切换而不同,后者AppUIConfig是针对日语版本的被测软件中UI的记录。

 当以上工作都完成后,就需要修改你的测试代码来将所有使用name来寻找的控件改为对应语言的配置项的值。

多语言的自动化测试最费时的工作就是去创建和维护上面的这个xml,而且每次UI的变动都要去切换多个语言去确认和修改。这么费事的原因就是被测软件没有一个统一的资源文件来记录多语言的信息,如果开发可以使用一种资源文件记录多语言信息,来构建多语言版本,那么测试程序就不用自己去维护一个超大的xml来记录这些信息了,就可以读取每个build附带的资源文件来寻找对应语言的控件名字。