はじめに
UiPath Studioのコード実装機能を活用し、複数のWEBサイトのテストを共通のコードで実行しよう、という試みです。
当記事は以下の記事の実装をベースに、
並列処理の扱いを組み込むことで高速化するアイデアを試します。
結果から…。並列処理で処理時間 1/3超 の高速化に成功しました。
改善前
改善後
約51秒 が 約15秒 で完了しています。
仕組み上は最高速度を目指すことは不可能であるものの、
実用レベルには十分な速度は実現できたと思います。
余談ですが、前回のコードをStudio v23.10 ⇒ v24.10 で実行するだけでも 10秒弱 高速化していました。
v23.10では約60秒掛かっていた処理が、改善前の図のとおり51秒程度の結果でした。
実装したコード
Excelレイアウトも少し見直しました。
並列処理の指示を明確にするために専用の列を設けました。
並列指示が途切れるまでを1つの処理ブロックとしてまとめて処理する設計思想です。
設計思想として、Excelパラメータで判断するか、共通コードのロジック側で判断するか、といった判断は様々考えられます。
今回は少し考えたのちExcel側で指示する仕様としました。
コード
using Code実装の検証.ObjectRepository;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using UiPath.CodedWorkflows;
using UiPath.Core;
using UiPath.Core.Activities.Storage;
using UiPath.Orchestrator.Client.Models;
using UiPath.Testing;
using UiPath.Testing.Activities.TestData;
using UiPath.Testing.Activities.TestDataQueues.Enums;
using UiPath.Testing.Enums;
using UiPath.UIAutomationNext.API.Contracts;
using UiPath.UIAutomationNext.API.Models;
using UiPath.UIAutomationNext.Enums;
namespace Code実装の検証
{
/***
* 操作をまとめて定義するクラス
*/
public static class OperationUitl {
/**
* ~Opitionは各操作に応じたオプション指定です。エラー前に待つのが嫌なのでゼロ指定した後、少し調整しています。
* 画面の描画時間に応じてカスタマイズが必要です。
*/
public static ClickOptions click => new ClickOptions() {
DelayBefore = 0,
DelayAfter = 0,
Timeout = 0
};
public static GetTextOptions getText => new GetTextOptions() {
DelayBefore = 0,
DelayAfter = 0,
Timeout = 1
};
/**
* シミュレート入力で画面を開くオプション指定
*/
public static TargetAppOptions simulate => new TargetAppOptions() {
InteractionMode = NInteractionMode.Simulate,
OpenMode = NAppOpenMode.IfNotOpen,
AttachMode = NAppAttachMode.SingleWindow
};
/**
* WindowMessage入力で画面を開くオプション指定
*/
public static TargetAppOptions winMsg => new TargetAppOptions() {
InteractionMode = NInteractionMode.WindowMessages,
OpenMode = NAppOpenMode.IfNotOpen,
AttachMode = NAppAttachMode.SingleWindow
};
}
public class TestByExcel : CodedWorkflow
{
[TestCase]
public void Execute(){
IDictionary<string, object> returnVal =RunWorkflow("readExcel.xaml",
new Dictionary<string, object>(){
{ "sheetName", "rpachallenge"} });
System.Data.DataTable dt = (System.Data.DataTable)returnVal["OutDt"];
List<String[]> 並列操作List =new List<String[]>();
int i=0;
var result = "";
bool is並列シミュレート = false;
foreach (System.Data.DataRow row in dt.Rows ) {
bool 並列指示 = false;
try {
並列指示 = Convert.ToBoolean(row["並列"].ToString());
}catch (Exception e){
;
}
// 並列指示の場合、実際の操作は次のループに任せる
if (並列指示) {
// 操作に必要な情報をString配列でListに保持しておく
並列操作List.Add( new String[]{row["操作"].ToString(),row["要素名"].ToString(),row["値"].ToString()} );
is並列シミュレート = Convert.ToBoolean(row["シミュレート"]);
} else {
// 並列操作Listが溜まっていれば…
if ( 並列操作List.Count>0 ) {
// バッチ操作をしてListも初期化する
バッチ操作(row["画面名"].ToString(), 並列操作List, is並列シミュレート);
並列操作List = new List<String[]>();
}
// 通常(=並列以外)の操作実装
if ("contains".Equals(row["操作"].ToString())) {
testing.VerifyExpression(result.Contains(row["値"].ToString()));
} else {
result = 操作(row["画面名"].ToString(),row["操作"].ToString(),row["要素名"].ToString(),row["値"].ToString(),Convert.ToBoolean(row["シミュレート"]) );
}
}
i++;
}
}
public void バッチ操作(String screen, List<String[]> inputList , bool isSimulate)
{
UiTargetApp uiScreen ;
if (isSimulate) {
uiScreen = uiAutomation.Open(screen, OperationUitl.simulate);
} else {
uiScreen = uiAutomation.Open(screen, OperationUitl.winMsg);
}
// 同時実行タスクの最大数を Listサイズ に設定
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = inputList.Count;
// 入力操作を並行で…
Parallel.ForEach(inputList, parallelOptions, x =>
{
switch(x[0]) {
case "click":
uiScreen.Click(x[1] ,OperationUitl.click);
break;
case "input":
uiScreen.TypeInto( x[1], x[2] );
break;
}
});
}
public string 操作(String screen, String operation, String element, String value, bool isSimulate)
{
UiTargetApp uiScreen ;
if (isSimulate) {
uiScreen = uiAutomation.Open(screen, OperationUitl.simulate);
} else {
uiScreen = uiAutomation.Open(screen, OperationUitl.winMsg);
}
switch(operation) {
case "click":
uiScreen.Click(element ,OperationUitl.click);
break;
case "input":
uiScreen.TypeInto( element, value );
break;
case "get":
return uiScreen.GetText(element , OperationUitl.getText);
break;
case "close":
uiScreen.Dispose();
Log( "close all." , LogLevel.Trace);
RunWorkflow("closeAll.xaml");
break;
case "screenshot":
Log( "screenshot." , LogLevel.Trace);
RunWorkflow("screenshot.xaml");
break;
case "delay":
System.Threading.Thread.Sleep(int.Parse( value ));
break;
}
return "";
}
}
}
前回からの変更の概要説明
関数名の「バッチ操作」は並列処理を実装するか決めていない時の名残りで、
バッチ的に画面アタッチ回数を減らすだけでは効果がなく、並列処理を実装した経緯によるものです。
触れていませんが今回の改善によりもう一方のWEBテストであるUiBank向けのテストも退行していないことも確認しました。
逆に言えば、UiBank, RPA Challenge以外のWEBサイトでの動作は確認していません。
対象とするシステムの特徴の幅広さに応じて、細かい修正や調整が必要となると考えられます。
おわりに
個人的に想定した課題はクリアしました。当件の実装はここまでとします。
簡単なWEBの操作であればExcelのファイルを書き換えるだけで操作が可能であろう、と考えられます。
UiPath Studioの実装者がアサインできず、テスト実装が課題になるようなユースケースはあるのでは…と想像します。そんな際に一部の機能を共通コード等でフレームワーク的に提供することで、効率化できるかもしれません。
課題解決の参考になれば幸いです。