頻繁なリリースでAPIが破損するのを防ぐ方法、またはオープンAPIの自動テストを作成して結果をTelegramボットで保護する方法

画像



まえがき



私たちのチームは、オープンペイメントAPIを含む金融商品を開発し、継続的インテグレーションの実践に取り組む多くのプロジェクトと同様に、3年前にプロジェクトを作成しながら、プロジェクトのテストカバレッジを改善し、かなり頻繁にコードの最大の安定性を達成する方法について考え始めました変更(製品環境に1日に数回更新プログラムをインストールすることがあります)。 これは、3つの側面で特に重要です。





この記事では、私の経験を共有し、サーバーとサーバーの相互作用とブラウザーを介した作業の両方を含むAPIインターフェースのテストを開発する方法の例を示します。 実証するために、私たちの支払いゲートウェイを介して銀行カードによる支払いのプロセスをテストし、テスト結果をTelegramに送信する簡単な例を示します。



GitHubでテスト例付きのプロジェクトをダウンロードできます: https : //github.com/cloudipsp/auto_tests.git



テスト支払いAPIのドキュメント: https : //www.fondy.eu/info/api/



環境を整えます



ロボットフレームワークを使用してテストを開発します。このフレームワークには独自のRIDE開発環境がありますが、利便性と機能の点ではPyCharmより大幅に劣っています。



乗る



画像



開発を開始するには、インストールします



  1. virtualenv



    pip install virtualenv setuptools
          
          





  2. pycharm eduの無料版

    https://www.jetbrains.com/pycharm-edu/download/
  3. PyCharmのプラグインを置く

    インテリボット:

    http://plugins.jetbrains.com/plugin/7386?pr=pycharm111



    ロボットフレームワークのサポート:

    http://plugins.jetbrains.com/plugin/7415?pr=pycharm99



    同時に、PyCharm Edu 2.0.4の現在のバージョンでは、最後の0.15には互換性がないため、ロボットフレームワークバージョン0.14.2をインストールする必要がありました。
  4. githubを使用してプロジェクトのクローンを作成します。すべてを最初からやりたい場合は、この項目をスキップできます。



     git clone https://github.com/cloudipsp/auto_tests.git
          
          





  5. 依存関係のインストール:



    まず第一に、これらのライブラリで十分です。



    robotframework==2.9a1

    selenium

    robotframework-selenium2library

    requests







    このコンテンツでpip-requires.txtファイルを作成し、virtualenvをアクティブにして、インストールします



     cd auto_tests pip install -r pip-requires.txt
          
          







開発:ブラウザなしの自動テスト





たとえば、マーチャント側でカードが入力されたときに3DSecureカードを使用して購入のタイプを考えてみましょう(APIセクションhttps://www.fondy.eu/ru/info/api/v1.0/4 )。







簡単にするために、ステップ2を除外します。ブラウザでリダイレクトします(次の例でテストします)。 テストカードの場合、このリダイレクトはページ(常に同じ結果を返すバンキングエミュレーター)で行われ、パスワードが正しく入力されます。



この場合、2つのステップがあります。





初期設定ファイル



https://www.fondy.eu/ru/info/api/v1.0/2のドキュメントで、テストするテストカードを取得します 。 ちなみに、すべてのテスト支払いは販売者のデモアカウントで確認できます: https : //www.fondy.eu/mportal/#/account/demo







ロボットファイルを作成します。



cards.robot



 *** Settings *** Documentation A resource file with test credit cards. Imported once in resource.robot *** Variables *** #Name Card Number Exp Month Exp Year Cvv2 @{3dsApproved} 4444555566661111 01 24 238 @{no3dsApproved} 4444555511116666 01 24 238 @{3dsDeclined} 4444111166665555 01 24 238 @{no3dsDeclined} 4444111155556666 01 24 238
      
      





Merchant.robot



 *** Settings *** Documentation A resource file with test merchants. Imported once in resource.robot *** Variables *** ${TestMerchant} 1397120 #(Test merchant)
      
      





variables.robot



 *** Settings *** Documentation Variables used in all tests. Imported one time in resource.robot *** Variables *** ${API SERVER} api.fondy.eu ${JSON} application/json ${XML} application/xml ${FORM} application/x-www-form-urlencoded
      
      





resource.robot



 *** Settings *** Documentation A resource file with reusable keywords. Resource variables.robot Resource cards.robot Resource merchants.robot Library helper/utils.py Library requester.py
      
      





プロトコル仕様



APIへのリクエストとAPIからのレスポンスが仕様を満たしていることを確認するには、ドキュメントで説明されているパラメーターの構造を含むファイルspecification_settings.pyを作成します。 たとえば、ドキュメントのパラメーター







構造に対応します:



 PAY_SERVER2SERVER_3DS = { 'request_step1': { "order_id": { "required": True, "type": "string", "size": 1024 }, "merchant_id": { "required": True, "type": "int", "size": 12 }, "order_desc": { "required": True, "type": "string", "size": 1024 },
      
      





完全なspecification_settings.py
 PAY_SERVER2SERVER_3DS = { 'request_step1': { "order_id": { "required": True, "type": "string", "size": 1024 }, "merchant_id": { "required": True, "type": "int", "size": 12 }, "order_desc": { "required": True, "type": "string", "size": 1024 }, "signature": { "required": True, "type": "string", "size": 40 }, "amount": { "required": True, "type": "amount", "size": 12 }, "currency": { "required": True, "type": "string", "size": 3 }, "version": { "default": "1.0", "required": False, "type": "string", "size": 10 }, "server_callback_url": { "required": False, "type": "url", "size": 2048 }, "lifetime": { "required": False, "type": "int", "size": 6 }, "merchant_data": { "required": False, "type": "string", "size": 2048 }, "preauth": { "default": False, "type": "boolean", "required": False }, "sender_email": { "required": False, "type": "email", "size": 50 }, "lang": { "required": False, "type": "string", "size": 2 }, "product_id": { "required": False, "type": "string", "size": 1024 }, "verification": { "default": False, "type": "boolean", "required": False }, "card_number": { "required": True, "type": "string", "size": 19 }, "cvv2": { "required": True, "type": "string", "size": 3 }, "expiry_date": { "required": True, "type": "date", "size": 4, "important": False, }, }, 'request_step2': { "order_id": { "required": True, "type": "string", "size": 1024 }, "merchant_id": { "required": True, "type": "int", "size": 12 }, "pares": { "required": True, "type": "string", "size": 20480 }, "md": { "required": True, "type": "string", "size": 1024 }, "version": { "default": "1.0", "required": False, "type": "string", "size": 10 }, "signature": { "required": True, "type": "string", "size": 40 }, }, 'response_3ds': { "response_status": { "type": "string", "required": True, "size": 50 }, "acs_url": { "type": "string", "required": True, "size": 2048 }, "pareq": { "type": "string", "required": True, "size": 20480 }, "md": { "default": "", "type": "string", "required": True, "description_en": "", "description_ru": "", "size": 1024 }, }, 'response_final': { "order_id": { "type": "string", "size": 1024 }, "merchant_id": { "type": "int", "size": 12 }, "amount": { "type": "amount", "size": 12 }, "currency": { "type": "string", "size": 3 }, "order_status": { "type": "string", "size": 50 }, "response_status": { "type": "string", "size": 50 }, "signature": { "type": "string", "size": 40 }, "tran_type": { "type": "string", "size": 50 }, "sender_cell_phone": { "type": "string", "size": 20 }, "sender_account": { "type": "string", "size": 50 }, "masked_card": { "type": "string", "size": 19 }, "card_bin": { "type": "int", "size": 6 }, "card_type": { "type": "string", "size": 50 }, "rrn": { "type": "string", "size": 50 }, "approval_code": { "type": "string", "size": 6 }, "response_code": { "type": "int", "size": 4 }, "response_description": { "type": "string", "size": 1024 }, "reversal_amount": { "type": "amount", "size": 12 }, "settlement_amount": { "type": "amount", "size": 12 }, "settlement_currency": { "type": "string", "size": 3 }, "order_time": { "type": "time", "size": 19 }, "settlement_date": { "type": "time", "size": 10 }, "eci": { "type": "string", "size": 2 }, "fee": { "type": "amount", "size": 12 }, "payment_system": { "type": "string", "size": 50 }, "sender_email": { "type": "email", "size": 254 }, "payment_id": { "type": "int", "size": 19 }, "actual_amount": { "type": "amount", "size": 12 }, "actual_currency": { "type": "string", "size": 3 }, "product_id": { "type": "string", "size": 1024 }, "merchant_data": { "type": "string", "size": 2048, }, "verification_status": { "type": "string", "size": 48, }, "rectoken": { "type": "string", "size": 48, }, "rectoken_lifetime": { "type": "time", "size": 19, }, }, }
      
      







次に、関数を作成します。



specification_settings.py仕様ファイルを実行し、異なるタイプのすべてのデータのセットからJSON、XML、FORM形式で要求を作成し、それらを最大長に仕上げる関数。



必要なパラメーターの作成
  def build_required_parameters_dict(self, merchant_id, currency, spec, spec_dict, response_url=None, *args, **kwargs): self.merchant_id = merchant_id request_params_specs = getattr( specifications_settings, spec)[spec_dict] # for requests with cards if args: kwargs['card_number'] = args[0] kwargs['expiry_date'] = int(str(args[1]) + str(args[2])) kwargs['cvv2'] = args[3] request_params = {} for param in request_params_specs: if param in kwargs.iterkeys(): request_params[param] = kwargs[param] elif param == "signature": request_params[param] = '' elif param == "currency": request_params[param] = currency elif param == "payment_systems": request_params[param] = 'card' elif param == "response_url": request_params[param] = response_url elif param == "merchant_id": request_params[param] = merchant_id elif param == "delayed": request_params[param] = "n" elif param == "order_desc": request_params[param] = 'test' + randomStr(size=7, chars=string.digits) elif param == "order_id": request_params[param] = self.order_id # for 3ds requests elif param == "pares": request_params[param] = TEST_PARES elif param == "md": request_params[param] = self.md # any other parameters elif request_params_specs[param]["type"] == "email": request_params[param] = "test@fondy.eu" elif request_params_specs[param]["type"] == "string": request_params[param] = randomStr( request_params_specs[param]["size"], param).encode('utf-8') elif request_params_specs[param]["type"] == "url": request_params[param] = "https://" + randomStr( request_params_specs[param]["size"] - 12, param).encode('utf-8') + ".com" elif request_params_specs[param]["type"] == "int": request_params[param] = randomStr(request_params_specs[param]["size"], "", string.digits) elif request_params_specs[param]["type"] == "amount": request_params[param] = randomStr( 5, "", string.digits) elif request_params_specs[param]["type"] == "boolean": request_params[param] = randomStr( 1, "", "YN") self.request_params = request_params
      
      







APIにデータを送信する直接HTTPS POSTの機能:



リクエストを送信
  def send_request(self, content_type, url=None, data=None, protocol=False, **kwargs): requests.packages.urllib3.disable_warnings() print "*HTML* sending request" print "*HTML* content_type=%r, url=%r, data=%r, kwargs=%r" % (content_type, url, data, kwargs) if data is None: data = self.request_params if self.order_id == '': data['order_id'] = 'test' + randomStr( 10, "", string.ascii_letters) else: data['order_id'] = self.order_id data['signature'] = "" data['signature'] = build_signature(self.request_params) self.save_order_id_from_server(data['order_id']) post_data = self.build_request(content_type, data) print "*HTML* POSTREQUEST %s" % (post_data) self.response = requests.post( url, headers={'Content-Type': content_type}, data=post_data, verify=False).text print "*HTML* POSTRESPONSE %s" % (self.response) return self.response
      
      







APIからの応答をチェックする関数も必要です。この関数は、受け取ったすべてのパラメーターをspecification_settings.py 仕様ファイルと比較します。



応答ステータスを確認する
  def verify_response_status(self, spec, spec_dict, content_type, response=None, request_params=None, status='approved'): try: if response == None: response = self.response print "*HTML* response %s" % (response) if request_params == None: if self.request_params: request_params = self.request_params print "*HTML* REq_par %s" % (request_params) response_params_specs = getattr( specifications_settings, spec)[spec_dict] print "*HTML* REsponse_par_spec %s" % (response_params_specs) errors_list = [] error = False response_params = parse_response(self.response, content_type) print "*HTML* REsp_par %s" % (response_params) for param in response_params_specs: if response_params[param] is not None: if response_params_specs[param]["type"] == "string": if len(response_params[param]) > response_params_specs[param]["size"]: errors_list.append('Error: size of param ' + param + ' is ' + str( len(response_params[param])) + ' but max is ' + str( response_params_specs[param]["size"])) error = True elif response_params_specs[param]["type"] == "int": if len(str(response_params[param])) > response_params_specs[param]["size"]: errors_list.append('Error: size of param ' + param + ' is ' + str( len(str(response_params[param]))) + ' but max is ' + str( response_params_specs[param]["size"])) error = True if response_params[param] != "" and not str(response_params[param]).isdigit(): errors_list.append( 'Error: param ' + param + ' is not integer') error = True else: errors_list.append('Error: param ' + param + ' is missing') error = True if request_params.get(param) is not None and request_params.get( param) != "" and param != 'signature' and response_params.get(param) is not None: if (response_params_specs[param]["type"] == "string" and request_params.get( param) != response_params.get( param)) or ( response_params_specs[param]["type"] == "amount" and int( request_params.get(param)) != int( response_params.get(param))): request = 'request:' + str(request_params.get(param)) response = 'response:' + str(response_params.get(param)) order_id = 'order_id:' + \ str(response_params.get('order_id')) errors_list.append( 'Error: param ' + param + ' is not equal in request and ' 'response\n request=%s\n response=%s order_id=%s' % ( request, response, order_id)) error = True if response_params_specs.get('signature') is not None: params_sign = {param: response_params.get(param, "") for param in response_params_specs if param != 'signature'} params = collections.OrderedDict(sorted(response_params.items())) params_sign['signature'] = build_signature(params_sign) if params_sign['signature'] != params["signature"]: errors_list.append('Error: signature invalid in response ') error = True if response_params.get('order_status') and response_params.get('order_status') != status: errors_list.append('Error: invalid status in response ') error = True except Exception as e: errors_list.append("final %s" % e.message) error = True finally: if error: raise Exception("*HTML* Errors:\n %s" % errors_list) else: print "*HTML* test passed OK"
      
      







そして、パラメーターをステップ2に渡すためにステップ1でAPIからの応答を保存する最後の関数:



3dsのmd pareqおよびacs urlを保存します
  def save_order_id_from_server(self, order_id): self.order_id = order_id print "*HTML* Order_id %s" % (self.order_id)
      
      







これらの関数に基づいて、テストスクリプトpay_with_3ds_card.robotを作成できます。



 *** Settings *** Documentation A test suite containing tests related to server-server complete purchase with 3ds card. Test Template Server-server full purchase with 3ds card Should Pass Test Timeout 15 seconds Default Tags smoke 3ds Library DebugLibrary Resource ../resource.robot *** Variables *** ${specificatons} PAY_SERVER2SERVER_3DS ${req_dict_step1} request_step1 ${resp_dict_step1} response_3ds ${url_step1} https://${API SERVER}/api/3dsecure_step1/ ${req_dict_step2} request_step2 ${resp_dict_step2} response_final ${url_step2} https://${API SERVER}/api/3dsecure_step2/ ***Test Cases *** merchant_id currency content_type credit_card USD_JSON_Approved ${TestMerchant} USD ${JSON} @{3dsApproved} USD_XML_Approved ${TestMerchant} USD ${XML} @{3dsApproved} USD_FORM_Approved ${TestMerchant} USD ${FORM} @{3dsApproved} UAH_JSON_Approved ${TestMerchant} UAH ${JSON} @{3dsApproved} UAH_XML_Approved ${TestMerchant} UAH ${XML} @{3dsApproved} UAH_FORM_Approved ${TestMerchant} UAH ${FORM} @{3dsApproved} EUR_JSON_Approved ${TestMerchant} EUR ${JSON} @{3dsApproved} EUR_XML_Approved ${TestMerchant} EUR ${XML} @{3dsApproved} EUR_FORM_Approved ${TestMerchant} EUR ${FORM} @{3dsApproved} RUB_JSON_Approved ${TestMerchant} RUB ${JSON} @{3dsApproved} RUB_XML_Approved ${TestMerchant} RUB ${XML} @{3dsApproved} RUB_FORM_Approved ${TestMerchant} RUB ${FORM} @{3dsApproved} GBP_JSON_Approved ${TestMerchant} GBP ${JSON} @{3dsApproved} GBP_XML_Approved ${TestMerchant} GBP ${XML} @{3dsApproved} GBP_FORM_Approved ${TestMerchant} GBP ${FORM} @{3dsApproved} *** Keywords *** Server-server full purchase with 3ds card Should Pass [Arguments] ${merchant_id} ${currency} ${content_type} @{credit_card} Build required parameters dict ${merchant_id} ${currency} ${specificatons} ${req_dict_step1} @{credit_card} Send request ${content_type} ${url_step1} Verify response status ${specificatons} ${resp_dict_step1} ${content_type} Save md pareq and acs url for 3ds ${content_type} Build required parameters dict ${merchant_id} ${currency} ${specificatons} ${req_dict_step2} Send request ${content_type} ${url_step2} Verify response status ${specificatons} ${resp_dict_step2} ${content_type}
      
      





この人間が読み取れるスクリプトは、5つの異なる通貨(USD、UAH、EUR、RUB、GBP)でサポートされている3つのJSON、XML、FORM要求形式すべてをテストします。



virtualenvでテストを実行します。



 (tests) E:\work\fondy\auto_tests>pybot server-server-tests
      
      









開発:ブラウザーとTelegramを使用した自動テスト



次に、作業するすべてのHTML要素を書き留めるロボットファイルを追加します:入力または分析

ui_repository.robot



 *** Settings *** Documentation Variables used in all tests. Imported one time in resource.txt *** Variables *** # Checkout page ${CHECKOUT_BUTTON} css=.btn-lime ${CVV2} id=cvv2 ${EXPIRE_YEAR} id=expire_year ${EXPIRE_MONTH} id=expire_month ${CARD_NUMBER} name=card_number ${CARD_NUMBER_FIELD} id=credit_card_number ${3DS_SUBMIT_BUTTON} xpath=//button[@type='submit'] #Response page ${ORDER_STATUS} css=.field_order_status .value ${TABLE_RESPONSE} id=table_response
      
      





resource.robotファイルに、 Selenium2Libraryライブラリーとブラウザーのopen関数を追加します。



 *** Settings *** Documentation A resource file with reusable keywords. Resource variables.robot Resource cards.robot Resource merchants.robot Resource ui_repository.robot Library Selenium2Library Library helper/utils.py Library requester.py *** Keywords *** Open Browser For Empty Page [Arguments] Open Browser about:blank Maximize Browser Window
      
      





variables.robotファイルに、ブラウザー名FireFoxを追加します。



 *** Settings *** Documentation Variables used in all tests. Imported one time in resource.robot *** Variables *** ${API SERVER} api.fondy.eu ${RESP_URL} https://${API SERVER}/test/responsepage/ ${SERVER} fondy.eu ${BROWSER} FireFox ${JSON} application/json ${XML} application/xml ${FORM} application/x-www-form-urlencoded
      
      





仕様ファイルは、ドキュメントhttps://www.fondy.eu/ru/info/api/v1.0/3からの新しいパラメーターセットで更新されました。 これらの仕様は、カードの詳細がマーチャントによって送信されないという点で異なりますが、マーチャントのWebサイトからリダイレクトされた後、支払いゲートウェイ側で入力されます。



specification_settings.py
 # -*- coding: utf-8 -*- PURCHASE_FIELDS_REDIRECT = { "request": { "order_id": { "type": "string", "required": True, "size": 1024 }, "merchant_id": { "type": "int", "required": True, "size": 12 }, "order_desc": { "type": "string", "required": True, "size": 1024 }, "signature": { "type": "string", "required": True, "size": 40 }, "amount": { "type": "amount", "required": True, "size": 12 }, "currency": { "type": "string", "required": True, "size": 3 }, "version": { "default": "1.0", "type": "string", "required": False, "size": 10 }, "response_url": { "type": "url", "required": False, "size": 2048 }, "server_callback_url": { "type": "url", "required": False, "size": 2048 }, "payment_systems": { "type": "string", "required": False, "size": 1024 }, "default_payment_system": { "type": "string", "required": False, "size": 25 }, "lifetime": { "default": "36000", "type": "int", "required": False, "size": 6 }, "merchant_data": { "type": "string", "required": False, "size": 2048 }, "preauth": { "default": False, "type": "boolean", "required": False }, "sender_email": { "type": "string", "required": False, "size": 50 }, "delayed": { "default": True, "type": "boolean", "required": False }, "lang": { "type": "string", "required": False, "size": 2 }, "product_id": { "type": "string", "required": False, "size": 1024 }, "required_rectoken": { "default": False, "type": "boolean", "required": False }, "verification": { "default": False, "type": "boolean", "required": False }, "verification_type": { "default": "amount", "type": "string", "required": False, "size": 25 }, "rectoken": { "type": "string", "required": False, "size": 40 }, "receiver_rectoken": { "type": "string", "required": False, "size": 40 }, "design_id": { "type": "string", "required": False, "size": 6 }, "subscription": { "default": False, "type": "boolean", "required": False }, "subscription_callback_url": { "type": "url", "required": False, "size": 2048 } }, "response": PAY_SERVER2SERVER_3DS['response_final'], }
      
      







テストスクリプトのすべてのファイルを詳細に説明するのではなく、理解するのは非常に簡単です。1つだけ説明します。



pay_with_checkout_url_3ds_approved.robot



pay_with_checkout_url_3ds_approved.robot
 *** Settings *** Documentation A test suite containing tests related to recurring api transactions with token. ... Card with 3ds. Suite Setup Open Browser For Empty Page Suite Teardown Close Browser Default Tags 3ds approved Test Template Checkout With 3ds Should Pass Resource checkout_resources.robot *** Variables *** ${specificatons} PURCHASE_FIELDS_REDIRECT ${req_dict_step1} request ${resp_dict_step1} response ${url} https://${API SERVER}/api/checkout/url/ ${checkout_url} ${EMPTY} ***Test Cases*** currency merchant_id message content_type credit_card USD_JSON_Approved USD ${TestMerchant} approved ${JSON} @{3dsApproved} USD_XML_Approved USD ${TestMerchant} approved ${XML} @{3dsApproved} USD_FORM_Approved USD ${TestMerchant} approved ${FORM} @{3dsApproved} UAH_JSON_Approved UAH ${TestMerchant} approved ${JSON} @{3dsApproved} UAH_XML_Approved UAH ${TestMerchant} approved ${XML} @{3dsApproved} UAH_FORM_Approved UAH ${TestMerchant} approved ${FORM} @{3dsApproved} EUR_JSON_Approved EUR ${TestMerchant} approved ${JSON} @{3dsApproved} EUR_XML_Approved EUR ${TestMerchant} approved ${XML} @{3dsApproved} EUR_FORM_Approved EUR ${TestMerchant} approved ${FORM} @{3dsApproved} RUB_JSON_Approved RUB ${TestMerchant} approved ${JSON} @{3dsApproved} RUB_XML_Approved RUB ${TestMerchant} approved ${XML} @{3dsApproved} RUB_FORM_Approved RUB ${TestMerchant} approved ${FORM} @{3dsApproved} GBP_JSON_Approved GBP ${TestMerchant} approved ${JSON} @{3dsApproved} GBP_XML_Approved GBP ${TestMerchant} approved ${XML} @{3dsApproved} GBP_FORM_Approved GBP ${TestMerchant} approved ${FORM} @{3dsApproved} *** Keywords *** Checkout With 3ds Should Pass [Arguments] ${currency} ${merchant_id} ${message} ${content_type} @{credit_card} Get and set checkout url ${merchant_id} ${currency} ${specificatons} ${req_dict_step1} ${RESP_URL} ${content_type} ${url} @{credit_card} Go to ${checkout_url} Input and submit checkout ${merchant_id} @{credit_card} Confirm 3ds ${merchant_id} Response page should be displayed Check transaction status ${message}
      
      







結果をTelegramに送信するには、リスナーと送信者の2つのファイルが必要です。



PythonListener.py
 from telegram_sender import * class PythonListener(object): ROBOT_LIBRARY_SCOPE = "GLOBAL" ROBOT_LISTENER_API_VERSION = 2 def __init__(self, count=0): self.ROBOT_LIBRARY_LISTENER = self self.count = count self.stat = None def end_suite(self, name, attrs): self.stat = attrs['statistics'] return self.stat def log_file(self, path): print self.stat test = Telegram() test.telegram_article(self.stat)
      
      







telegram_sender.py
 import telegram from helper._settings import * from telegram.ext import Updater class Telegram(object): def __init__(self, token=None): self.token = token or default_token self.updater = None self.bot = None def update_bot(self): self.updater = Updater(token=self.token) self.bot = telegram.Bot(token=self.token) self.updater.start_polling() self.bot.getMe() self.bot.getUpdates() def telegram_article(self, status): self.update_bot() # chat_id = bot.getUpdates()[-1].message.chat_id # add this string to update all telegram users chat_id = default_user self.bot.sendMessage(chat_id=chat_id, text=status) self.updater.stop()
      
      







また、_settings.pyにTelegramボットのパラメーターを書き留めます。



 default_token = None # Put Your bot token to this variable default_user = None # Add user chat id
      
      





トークンとチャットIDを取得する方法は、たとえばherehereで読むことができます



次に、実際にブラウザテストを実行します。 結果は電報で送られます:



 (tests) E:\work\fondy\auto_tests>pybot --listener PythonListener.py checkout-tests
      
      









あとがき



この記事が自動テスターと開発者の両方に役立つことを願っています。 次の記事では、すでに数千のテストがある場合の対処方法-テストを並列化する方法、テストの速度に関するメトリックを収集する方法、データベースと相互作用するテストを開発する方法についてお話します。



All Articles