Encontrar

記事
· 2024年9月23日 6m read

UnitTest(ユニットテスト)の自動化について考察

コミュニティの皆さんこんにちは。

突然ですが、皆さんはIRISの機能にある「ユニットテスト」は利用されているでしょうか。
筆者はまだ実装まで行えていませんが、各関数の品質保証を担保するため導入を検討している段階です。

現状、IRISのユニットテストには下記2点の対応すべき点があると考えています。

  1. テスト結果の可読性が低い(先日vscodeで拡張機能が出ていましたが、やはり見ずらいと感じました)
  2. ユニットテストを自動で実行する手段がない

特にテストが継続的に自動で実施されないと、ユニットテスト自体が次第に陳腐化し、実行されなくなり忘れ去られる恐れがあると考えます。
ただし、意味もなく定期的にテストを実行しても効果がありません。
そこで、Gitのpushのタイミングで行おうと考えました。

次にテスト環境です。
テスト環境の構築は、テスト自動化の観点からみるとCI/CDツール等を利用するのが一般的だと思います。
ただ今回は、テスト環境の構築を簡易にすませたいと考え、IRISの既存技術を組み合わせて構築しようと考えました。

そこで運用幅の広いInteroperabilityとユニットテストを組み合わせて、テストの自動化が可能か考察していきたいと思います。

 

【ユニットテスト全体概要】

 

 

【全体の流れ】

 ■ユーザの開発環境

  ①ユーザは改修したクラスをGitへpushする

 ■Git用のサーバ

  ②GitリモートリポジトリのHook「post-receive」が、テストサーバへHTTP通信を実施

 ■ユニットテスト用のサーバ

  ③RestAPIから②を受け取り、interoperabilityのサービス「ServiceRest」をキック

  ④Interoperability処理
    プロダクションは下記となっています。 ※主要な機能しかまだ作っていません。

    

プロダクション説明
項目 名称 説明
サービス ServiceRest RestAPIよりキックされ、プロセス「TestProcesse」をキックする。
「TestProcesse」が起動中の場合は、更新が重複するため何もしない。
  ServiceTimer 「TestProcesse」が動作中に発生したGitの更新は、定期的な監視を行っているこのサービスでフォローする
プロセス DeleteProcess 指定日を越えたユニットテストのデータと出力したファイルを削除する
  MainProcesse テスト結果を検証し、HTMLファイル・CSVファイルを出力する
オペレーション「SendMail」とプロセス「DeleteProcess」をキックする
  TestProcesse Gitの更新・テストクラスの配置・クラスのインポート・テスト実施を行う
プロセス「MainProcess」をキックする
オペレーション SendMail MainProcessよりテスト結果を受け、出力したファイルを添付してメールする

 


【一連の連携で追加した機能】

 ■テスト結果を参照するHTMLファイル・CSVファイルの作成
  IRISの機能であるテスト結果参照ページは、テスト結果の可読性が低いです。
  そこで、HTMLファイル・CSVファイルの出力を別途行うようにしました。


 ・既存のユニットテスト・ポータル画面
    

  TestAssertsまでドリルダウンしていかないと、エラーが発生の詳細な原因を判別するのが難しい。
  テスト全体を見渡すことが難しい。

 

 ・HTMLファイル出力

   

  テストデータをHTMLファイルに含んでいるため、スタンドアロンで表示させる事が可能。
  また、データ量が多くなる事を踏まえて、スクロールバーを下げる事で一定行数毎に結果を追加する仕様。
 

 ・CSVファイル出力

   

  ただのCSVファイルのため、特筆すべき点はありません。

 

■各テスト関数の処理時間を検証

 データの取得・登録を含んだ関数を改修した際、ごく稀に処理時間が想定外にかかるような場合があります。
 大概はロジックが悪いケースですが、処理時間の比較は割と見落としてしまう点だと思います。

 そこで、ユニットテストついでに処理時間の比較を行おうと考えました。
 ただ各テスト毎に処理時間の比較機能を組み込むのは、手間がかかるため困難です。

 そこで、ユニットテストの処理時間(関数実行時の処理時間)に着目しました。

 この「処理時間」をプロセスの処理で記録し、そこから得た中央値を利用して比較しようと考えています。
  ※注)テスト関数の処理時間を比較に利用するため、テスト関数内部の個々の処理自体に対する速度検証にはなりません。

 処理時間の比較は、下記流れにそって行っています。 ※フラグ制御にて比較自体を不可にもできる

   ①テスト関数のステータスが「passed(成功)」している事を確認する
   ②該当テスト関数が2回目以降である事を確認する(初回の場合は比較対象が無い為)
   ③過去の処理時間リストより、中央値を取得する
   ④ ③より、係数を利用して比較用の値を算出する
    係数は、テストクラスにパラメータとして設定します。パラメータ名は下記仕様に準じています。
     ・関数名 + 「ADD」→中央値にパラメータの値を加算する
     ・関数名 + 「MUL」→中央値にパラメータの値を乗算する
       

    テスト関数の係数(パラメータ)が設定されていない場合は、下記値を係数としています。
     ・プロセス「MainProcess」のプロパティ「BaseTime」に設定している値と中央値を乗算する

    ⑤ ④の値と比較し、今回実行時の処理時間が大きい場合、テスト関数のステータスを「passed」から「timeout」へ変更する
     timeoutへのステータス変更は、出力されるファイルへ反映する事になります。

       例)中央値から外れた場合のHTMLファイルの表現
        

    ⑥今回の処理時間を特定のグローバルに保存する。

 


【出力したファイルをメールの添付ファイルとして送付】

  オペレーション「SendMail」では、Embedded Pythonを利用してメールを送付しています。

  恥ずかしながら、interoperabilityのメール機能で複数のファイルを添付する方法が分からなかった為、Pythonで実行するように切り替えました。
  困ったら別の手段が検討できる、この柔軟さがIRISの良いところだと思います。

    例)エラーが発生した場合のメール内容

     

 

 


以上になります。

今回は考察が主なため、細かい作り込みは行っていません。
エラー処理や、GIT更新後のクラスインポート・コンパイルを更新があったクラスのみに絞る等々、まだまた実用化には長い道のりだと感じています。
他にも実装したいロジックはありますが、一旦ここで筆を置きたいと思います。


Interoperabilityの機能を使うことで、ユニットテストの自動化を実現する事は可能だと考えています。
拙筆ではありますが、何かの参考になれば幸いです。

サンプルPGはGithubに置いてあります。

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
記事
· 2024年9月23日 4m read

iris-DataViz: Tableau-style drag-and-drop Data Analysis and Visualization Application

image

Hi Community,

In this article, I will introduce my application iris-DataViz

iris-DataViz is an Exploratory Data Analysis and Visualization Streamlit Application that leverages the functionality of IRIS embedded python and SQLAlchemy to interact with IRIS, as well as the PyGWalker python library for data analysis and data Visualization. PyGWalker (Python Graphic Walker) is an interactive data visualization library built for Python, aiming to bring the ease and functionality of Tableau-style drag-and-drop visualization into Python environments.


Application Features 

  • Drag-and-Drop Visualization
  • Manipulate and clean your data within the visualization
  • Wide Range of Chart Types:
  • Interactive Data Exploration
  • Interactive Aggregation and Grouping:
  • Exportable Visualizations
  • Automatic Data Summarization
  • Calculated / Computed Fields
  • Support for Categorical, Numerical, and Temporal Data
  • Interactive Legends and Filters
  • Interactive Report Generation
  • Geospatial Data Support


Application Overview

Application already imported car-related data.
In order to view the data navigate to the Management Portal SQL and view the data by using below SQL command:

SELECT
Make, Model, Year, EngineFuelType, EngineHP,
EngineCylinders, TransmissionType, Driven_Wheels, 
NumberofDoors, MarketCategory, VehicleSize, 
VehicleStyle, highwayMPG, citympg, Popularity, MSRP
FROM DataViz.CarsInfo

image

To run the application Navigate to http://localhost:8051.
Select Namespace,Schema and table. Click on Data tab to analyze the data.
image

Select the Visualization tab, Drag the desired columns in X-Axis and Y-Axis, and select the desired graph type.
image


Application Code Snippet

Below objectscript code invoked from embedded Python to get IRIS namespaces:

Class dc.DataViz.Util Extends %RegisteredObject
{
ClassMethod getNameSpaces() As %String
{
 //init sql statement   
 set statement=##class(%SQL.Statement).%New()
 //Prepare Class Query
 set status=statement.%PrepareClassQuery("%SYS.Namespace","List")
 //Check the Error
 if $$$ISERR(status) 
 { 
    do $system.OBJ.DisplayError(status) 
 }
 //Execute the statement
 set resultset=statement.%Execute()
 set results = "1"
 while resultset.%Next() {
	//zw resultset    
    if results = "1" {
	    set results = resultset.%Get("Nsp")
	    }
    else
    {
    	set results = results _ "," _ resultset.%Get("Nsp")
    }
 }
 return results
}
}

Embedded Python Code invoking objectscript code:

  import iris
  ns = iris.cls('dc.DataViz.Util').getNameSpaces()
  namespaces = ns.split(",")

The code below will get schema and tables list:

 def get_schema(self):
        #Establish IRIS Connection    
        with self.engine.connect() as conn:
            with conn.begin():     
                sql = text(""" 
                    SELECT distinct TABLE_SCHEMA
                    FROM INFORMATION_SCHEMA.TABLES                   
                    WHERE TABLE_TYPE='BASE TABLE'       
                    order by TABLE_SCHEMA
                    """)
                results = []
                try:
                    #Fetch records into results variable
                    results = conn.execute(sql).fetchall()
                    schemas="0"
                    for element in results:                                 
                       if schemas == "0":
                           schemas = element[0]
                       else:
                           schemas = schemas+","+element[0]
                except Exception as e:
                    print(e)
                    
        return schemas

    def get_tables(self,schema):
        #Establish IRIS Connection       
        with self.engine.connect() as conn:
            with conn.begin():     
                sql = text(""" 
                    SELECT TABLE_NAME
                    FROM INFORMATION_SCHEMA.TABLES                   
                    WHERE TABLE_TYPE='BASE TABLE'
                    AND TABLE_SCHEMA = :schema
                     order by TABLE_NAME
                    """).bindparams(schema=schema)            
                results = []
                try:
                    #Fetch records into results variable
                    results = conn.execute(sql).fetchall()                  
                    tables="0"
                    for element in results:                                 
                       if tables == "0":
                           tables = element[0]
                       else:
                           tables = tables+","+element[0]
                except Exception as e:
                    print(e)
                    
        return tables


For more details, please visit iris-DataViz open exchange application page.

Thanks

3 Comments
ディスカッション (3)1
続けるにはログインするか新規登録を行ってください
質問
· 2024年9月23日

Large Stream to base64Encoded

I have below method , which receives a PDF ; this method throws error for a PDF file of 3MB size

 <MAXSTRING>zBase64Encode+9 -- logged as '-' number - @'    set encodedData = $system.Encryption.Base64Encode(content)'

How do I fix this.

----------------

ClassMethod Base64Encode(pStream As %Stream) As %Stream
{
  set tSC = $$$OK
  set tSC = pStream.Rewind()  
  s pEncoded=##class(%Stream.GlobalCharacter).%New()  
  while 'pStream.AtEnd {   
    Set tLen = pStream.Size    
    set content = pStream.Read(.tLen)    
    set encodedData = $system.Encryption.Base64Encode(content)
    set encodedData=$translate(encodedData, $c(13,10))
    s tSC=pEncoded.Write(encodedData)   
    q:$$$ISERR(tSC)
  }
  //q:$$$ISERR(tSC)  
  do pEncoded.Rewind()  
  Quit pEncoded
}

2 Comments
ディスカッション (2)1
続けるにはログインするか新規登録を行ってください
お知らせ
· 2024年9月23日

Time to vote in the InterSystems Developer Tools Contest

Hi Community,

It's voting time! Cast your votes for the best applications in our Developer Tools Contest:

🔥 VOTE FOR THE BEST APPS 🔥

How to vote? Details below.

Experts nomination:

InterSystems experienced jury will choose the best apps to nominate the prizes in the Experts Nomination.

Community nomination:

For each user, a higher score is selected from two categories below:

Conditions

Place
1st 2nd 3rd
If you have an article posted on DC and an app uploaded to Open Exchange (OEX) 9 6 3
If you have at least 1 article posted on DC or 1 app uploaded to OEX 6 4 2
If you make any valid contribution to DC (posted a comment/question, etc.) 3 2 1

 

Level

Place
1st 2nd 3rd
VIP Global Masters level or ISC Product Managers 15 10 5
Ambassador GM level 12 8 4
Expert GM level or DC Moderators 9 6 3
Specialist GM level 6 4 2
Advocate GM level or ISC Employees 3 2 1

Blind vote!

The number of votes for each app will be hidden from everyone. Once a day we will publish the leaderboard in the comments to this post. 

The order of projects on the contest page will be as follows: the earlier an application was submitted to the competition, the higher it will be on the list.

P.S. Don't forget to subscribe to this post (click on the bell icon) to be notified of new comments.

To take part in the voting, you need:

  1. Sign in to Open Exchange – DC credentials will work.
  2. Make any valid contribution to the Developer Community – answer or ask questions, write an article, contribute applications on Open Exchange – and you'll be able to vote. Check this post on the options to make helpful contributions to the Developer Community.

If you change your mind, cancel the choice and give your vote to another application!

Support the application you like!


Note: contest participants are allowed to fix the bugs and make improvements to their applications during the voting week, so don't miss and subscribe to application releases!

5 Comments
ディスカッション (5)5
続けるにはログインするか新規登録を行ってください
ダイジェスト
· 2024年9月23日

Publicações Desenvolvedores InterSystems, Setembro 16 - 22, 2024, Resumo