[AWS Labs探訪] ServerlessなUI自動テストを実行する

AWSのサービスを利用して、サーバーレスにUI自動テストを実行してみます。 AWS Labs探訪とは GitHubには、[awslabs]というAWSが公開しているOrganizationsがあります。ここにはAWSサー […]

広告ここから
広告ここまで

目次

    AWSのサービスを利用して、サーバーレスにUI自動テストを実行してみます。

    AWS Labs探訪とは

    GitHubには、[awslabs]というAWSが公開しているOrganizationsがあります。ここにはAWSサービスを利用したサンプルコードや新しいSDK、オフィシャルイベントで開催されたワークショップのサンプルコードといった様々なコードが公開されています。有名所では、SAMなどもこのOrganizationsにホストされています。

    ここにはそのまま使えるものからprodで使うなと書かれたものまで玉石混交です。AWS Labs探訪は、ここにDiveしてみて仕事に使えそうなものがないか探してみようという企画です。

    今回紹介するプロジェクト

    Serverless-Automated-UI-Testing

    CodePipelineやCodeBuildを用いてUI自動テストをサーバーレスに実行するプロジェクトです。

    プロジェクトをセットアップする

    まずはGitからダウンロードします。

    $ git clone https://github.com/awslabs/serverless-automated-ui-testing.git
    $ cd serverless-automated-ui-testing

    CodeCommitリポジトリの作成

    続いてCodeCommitのリポジトリを作成します。

    $ aws codecommit create-repository --repository-name automated-ui-testing --repository-description "Repository for Automated UI Testing and Continuous Delivery using CodePipeline."
    
    {
        "repositoryMetadata": {
            "repositoryName": "automated-ui-testing", 
            "cloneUrlSsh": "ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/automated-ui-testing", 
            "lastModifiedDate": 1557796573.16, 
            "repositoryDescription": "Repository for Automated UI Testing and Continuous Delivery using CodePipeline.", 
            "cloneUrlHttp": "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/automated-ui-testing", 
            "creationDate": 1557796573.16, 
            "repositoryId": "xxxxxxxxxxxxxxx", 
            "Arn": "arn:aws:codecommit:us-east-1:372284591230:automated-ui-testing", 
            "accountId": "xxxxxxxx"
        }
    }

    CloudFormationスタックの作製

    automated-ui-testing-params.jsonにSNSトピックやS3バケットの設定が記載されています。これを自分のアカウントの値に変更しておきましょう。

    [
      {
        "ParameterKey": "SourceRepo",
        "ParameterValue": "automated-ui-testing"
      },
      {
        "ParameterKey": "ApprovalTopic",
        "ParameterValue": "prod-approval-topic"
      },
      {
        "ParameterKey": "ProductionBucket",
        "ParameterValue": "production-s3-website-bucket"
      }
    ]
    

    設定ができたら、CloudFormationでデプロイします。

    $ aws cloudformation create-stack \
     --stack-name automateduitesting \
     --template-body file://automated-ui-testing.yaml \
     --parameters file://automated-ui-testing-params.json \
     --capabilities CAPABILITY_NAMED_IAM
    {
        "StackId": "arn:aws:cloudformation:us-east-1:9999999999:stack/automateduitesting/b5ddf160-75e6-11e9-bd46-0eee31812ac2"
    }

    作成に成功したら、以下のような形でCLIからデータが見れます。

    $ aws cloudformation describe-stacks --stack-name automateduitesting
    
    {
        "Stacks": [
            {
                "StackId": "arn:aws:cloudformation:us-east-1:372284591230:stack/automateduitesting/723b5980-75ea-11e9-bd46-0eee31812ac2", 
                "DriftInformation": {
                    "StackDriftStatus": "NOT_CHECKED"
                }, 
                "Description": "Template which creates automated UI testing using Selenium with AWS CodeBuild and AWS CodePipeline.\n", 
                "Parameters": [
                    {
                        "ParameterValue": "automated-ui-testing", 
                        "ParameterKey": "SourceRepo"
                    }, 
                    {
                        "ParameterValue": "prod-approval-topic", 
                        "ParameterKey": "ApprovalTopic"
                    }, 
                    {
                        "ParameterValue": "example.com", 
                        "ParameterKey": "ProductionBucket"
                    }
                ], 
                "Tags": [], 
                "Outputs": [
                    {
                        "Description": "URL of Status Website", 
                        "OutputKey": "StatusWebsite", 
                        "OutputValue": "https://status-website-automateduitesting.s3-website-us-east-1.amazonaws.com"
                    }, 
                    {
                        "Description": "URL of Test Website", 
                        "OutputKey": "TestWebsite", 
                        "OutputValue": "https://test-website-automateduitesting.s3-website-us-east-1.amazonaws.com"
                    }
                ], 
                "EnableTerminationProtection": false, 
                "CreationTime": "2019-05-14T01:49:01.163Z", 
                "Capabilities": [
                    "CAPABILITY_NAMED_IAM"
                ], 
                "StackName": "automateduitesting", 
                "NotificationARNs": [], 
                "StackStatus": "CREATE_COMPLETE", 
                "DisableRollback": false, 
                "RollbackConfiguration": {}
            }
        ]
    }

    –stack-nameに注意

    このスタックは、stack-nameの値を使ってS3 BucketとCognito Identify Poolを作成します。そのため、両リソースの命名制約に引っかかってしまうとスタック作成に失敗します。

    よくあるのは、汎用的なスタック名を利用して
    test-output-automated-ui-testing already exists
    のようなエラーを吐くパターンと、-(ハイフン)を利用してCognito Identity Poolのバリデーションに引っかかるパターンがあります。

    テストを実行する

    デプロイが終われば、あとはリリースパイプラインを走らせるだけです。

    GitのリポジトリをローカルにcloneしてCodeCommitにアップするか、CodePipelineの設定を変えて、Forkした自分のリポジトリにつないでやればよいでしょう。

    実際に使うときは、GitHubウェブフック。
    今回は後片付けを楽にしたかったのでpipeline側を選択

    これでソースがアップデートされた際に、ビルドパイプラインが実行され、Firefox / Chrome / PhantomJSでのUIテストが走り出します。

    PhantomJS?2年前のサンプルコードだから仕方ないです。気になるという人は、CloudFormationからCodeBuildの該当リソースを消してやりましょう。

    テスト結果はブラウザから確認可能

    UITest時、StatusページのデプロイがLambda経由で実行されます。

    そのため、CloudFormationのURL of Status WebsiteというOutputにあるURLへアクセスすると、CodeBuildでのテスト結果を確認することができます。

    ちなみにこのテーブルは、Cognito Identity Provider + DynamoDBでかなり力技で作られています。

    /*jshint esversion: 6 */
    
    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
        IdentityPoolId: 'Cognito_IDP_ID',
    });
    AWS.config.region = "AWS_REGION";
    var ddb = new AWS.DynamoDB.DocumentClient();
    var body, chrome_tab, firefox_tab;
    var firstload = 1;
    
    function getdata(modlist, params, brw){
      var flag;
      var tbl = document.createElement('table');
      if (brw == 'Chrome'){
        tbl.setAttribute('id', 'Chrome_table');
      }
      if (brw == 'Firefox'){
        tbl.setAttribute('id', 'Firefox_table');
      }
      if (brw == 'PhantomJS'){
        tbl.setAttribute('id', 'PhantomJS_table');
      }
      thead = tbl.createTHead();
      tr = thead.insertRow();
      td = tr.insertCell();
      td.appendChild(document.createTextNode(brw));
      td.style.fontSize = 'x-large';
      td.style.fontWeight = 'bolder';
      td.setAttribute('colSpan', '7');
      tr = thead.insertRow();
      tr.style.fontSize = 'large';
      tr.style.fontWeight = 'bolder';
      td = tr.insertCell();
      td.appendChild(document.createTextNode('Module'));
      td = tr.insertCell();
      td.appendChild(document.createTextNode('Testcase'));
      td = tr.insertCell();
      td.appendChild(document.createTextNode('Status'));
      td = tr.insertCell();
      td.appendChild(document.createTextNode('Start Time'));
      td = tr.insertCell();
      td.appendChild(document.createTextNode('End Time'));
      td = tr.insertCell();
      td.appendChild(document.createTextNode('Time Taken (ms)'));
      td = tr.insertCell();
      td.appendChild(document.createTextNode('Error Message'));
      ddb.scan(params, function (err, data) {
        if (err) console.error(err, err.stack); // an error occurred
        else {
          tdata = data.Items;
          console.log(tdata);
          for (var mod in modlist){
            var newmod = 1;
            for (var tc in modlist[mod]){
              if (newmod == 1){
                tr = tbl.insertRow();
                td = tr.insertCell();
                td.appendChild(document.createTextNode(mod));
                td.setAttribute('rowSpan', modlist[mod].length+1);
                newmod = 0;
              }
              flag = 1;
              for (i=0; i< tdata.length; i++){
                tcdetails = tdata[i].testcaseid.split('-');
                if (tdata[i].module == mod && tcdetails[0] == brw.toLowerCase() && tcdetails[1] == modlist[mod][tc]){
                  tr = tbl.insertRow();
                  td = tr.insertCell();
                  td.appendChild(document.createTextNode(modlist[mod][tc]));
                  td = tr.insertCell();
                  td.appendChild(document.createTextNode(tdata[i].details.Status));
                  if ( tdata[i].details.Status == 'Passed' ){
                    td.style.color = 'green';
                  }
                  else {
                    td.style.color = 'red';
                  }
                  td = tr.insertCell();
                  td.appendChild(document.createTextNode(tdata[i].details.StartTime));
                  td = tr.insertCell();
                  td.appendChild(document.createTextNode(tdata[i].details.EndTime));
                  td = tr.insertCell();
                  td.appendChild(document.createTextNode(tdata[i].details.TimeTaken));
                  td = tr.insertCell();
                  td.appendChild(document.createTextNode(tdata[i].details.ErrorMessage));
                  flag = 0;
                }
              }
              if (flag == 1){
                tr = tbl.insertRow();
                td = tr.insertCell();
                td.appendChild(document.createTextNode(modlist[mod][tc]));
                td = tr.insertCell();
                td.appendChild(document.createTextNode(' '));
                td = tr.insertCell();
                td.appendChild(document.createTextNode(' '));
                td = tr.insertCell();
                td.appendChild(document.createTextNode(' '));
                td = tr.insertCell();
                td.appendChild(document.createTextNode(' '));
                td = tr.insertCell();
                td.appendChild(document.createTextNode(' '));
              }
            }
          }
        }
      });
      return tbl;
    }
    
    
    function tableCreate() {
      var modparams = {
        TableName: 'Modules_Table',
      };
      var modlist = {};
      console.log(modparams);
      ddb.scan(modparams, function (err, data) {
        if (err) console.error(err, err.stack);
        else {
          var mdata = data.Items;
          for (var m in mdata){
            modlist[mdata[m].module] = mdata[m].testcases;
          }
        }
      });
      var browsers = ['Chrome', 'Firefox', 'PhantomJS'];
      var br;
      var tbl;
      var params;
      for (br in browsers){
        params = {
          TableName: 'Status_Table',
          FilterExpression: "begins_with(testcaseid, :tcid)",
          ExpressionAttributeValues: {
           ":tcid": browsers[br].toLowerCase()
          }
        };
        if (browsers[br] == 'Chrome'){
          ch_tbl = getdata(modlist, params, 'Chrome');
        }
        if (browsers[br] == 'Firefox'){
          ff_tbl = getdata(modlist, params, 'Firefox');
        }
        if (browsers[br] == 'PhantomJS'){
          pjs_tbl = getdata(modlist, params, 'PhantomJS');
        }
      }
      body.replaceChild(ff_tbl, document.getElementById('Firefox_table'));
      body.replaceChild(ch_tbl, document.getElementById('Chrome_table'));
      body.replaceChild(pjs_tbl, document.getElementById('PhantomJS_table'));
    }
    
    document.addEventListener("DOMContentLoaded", function(event) {
      body = document.body;
      if (firstload == 1){
        chrome_tab  = document.createElement('table');
        firefox_tab  = document.createElement('table');
        phantomjs_tab = document.createElement('table');
        chrome_tab.setAttribute('id', 'Chrome_table');
        firefox_tab.setAttribute('id', 'Firefox_table');
        phantomjs_tab.setAttribute('id', 'PhantomJS_table');
        body.appendChild(chrome_tab);
        body.appendChild(document.createElement('br'));
        body.appendChild(firefox_tab);
        body.appendChild(document.createElement('br'));
        body.appendChild(phantomjs_tab);
        tableCreate();
        firstload = 0;
      }
    });
    

    データソースはDynamoDBにあります。

    テストケースはどこでみる?

    tests/testsuite.pyがテストで実行されている様子です。

    #!/usr/bin/env python
    
    from __future__ import print_function
    from selenium import webdriver
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from datetime import datetime
    from botocore.exceptions import ClientError
    import requests
    import traceback
    import os
    import boto3
    import inspect
    import sys
    import random
    
    
    br = os.environ['BROWSER']
    main_url = os.environ['WebURL']
    module_table = os.environ['ModuleTable']
    status_table = os.environ['StatusTable']
    s3_output_bucket = os.environ['ArtifactBucket']
    ddb = boto3.client('dynamodb')
    s3 = boto3.client('s3')
    s3_path_prefix = os.environ['CODEBUILD_BUILD_ID'].replace(':', '/')
    
    
    def funcname():
        return inspect.stack()[1][3]
    
    
    def update_status(mod, tc, st, et, ss, er):
        if et != ' ':
            t_t = str(int(round((datetime.strptime(et, '%d-%m-%Y %H:%M:%S,%f') -
                                 datetime.strptime(st, '%d-%m-%Y %H:%M:%S,%f')).microseconds, -3) / 1000))
        else:
            t_t = ' '
        try:
            if er:
                ddb.update_item(Key={'module': {'S': mod}, 'testcaseid': {'S': br + '-' + tc}},
                                UpdateExpression="set details.StartTime = :st, details.EndTime = :e, details.#S = :s," +
                                "details.ErrorMessage = :er, details.TimeTaken = :tt",
                                ExpressionAttributeValues={':e': {'S': et}, ':s': {'S': ss}, ':st': {'S': st},
                                                           ':er': {'S': er}, ':tt': {'S': t_t}},
                                TableName=status_table, ExpressionAttributeNames={'#S': 'Status'})
            else:
                ddb.update_item(Key={'module': {'S': mod}, 'testcaseid': {'S': br + '-' + tc}},
                                UpdateExpression="set details.StartTime = :st, details.EndTime = :e, details.#S = :s," +
                                                 "details.TimeTaken = :tt",
                                ExpressionAttributeValues={':e': {'S': et}, ':s': {'S': ss}, ':st': {'S': st},
                                                           ':tt': {'S': t_t}},
                                TableName=status_table, ExpressionAttributeNames={'#S': 'Status'})
        except ClientError as e:
            if e.response['Error']['Code'] == 'ValidationException':
                ddb.update_item(Key={'module': {'S': mod}, 'testcaseid': {'S': br + '-' + tc}},
                                UpdateExpression="set #atName = :atValue", ExpressionAttributeValues={
                                ':atValue': {'M': {'StartTime': {'S': st}, 'EndTime': {'S': et}, 'Status': {'S': ss},
                                                   'ErrorMessage': {'S': er}, 'TimeTaken': {'S': t_t}}}},
                                TableName=status_table,
                                ExpressionAttributeNames={'#atName': 'details'})
            else:
                traceback.print_exc()
        except:
            traceback.print_exc()
    
    
    def tc001(browser, mod, tc):
        # Testcase to validate whether home page is displayed properly
        fname = mod + '-' + tc + '.png'
        starttime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
        endtime = ' '
        try:
            update_status(mod, tc, starttime, endtime, 'Started', ' ')
            browser.get(main_url)
            assert 'AWS CodeBuild automation' in browser.title
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'kp')))
            browser.get_screenshot_as_file(fname)
            with open(fname, 'rb') as data:
                s3.upload_fileobj(data, s3_output_bucket, s3_path_prefix + '/' + fname)
            os.remove(fname)
            print('Completed test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Passed', ' ')
        except:
            print('Failed while running test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Failed', traceback.print_exc())
            return -1
    
    
    def tc002(browser, mod, tc):
        # Testcase to validate whether button click is displayed properly
        fname = mod + '-' + tc + '.png'
        starttime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
        endtime = ' '
        todisplay = 'AWS CodeBuild is a fully managed build service that compiles source code,' + \
                    ' runs tests, and produces software packages that are ready to deploy.'
        try:
            update_status(mod, tc, starttime, endtime, 'Started', ' ')
            browser.get(main_url)
            assert 'AWS CodeBuild automation' in browser.title
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'kp')))
            browser.find_element_by_xpath("//*[@id='bc']/a").click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'displaybtn')))
            assert 'AWS CodeBuild automation - Button Click.' in browser.title
            browser.find_element_by_id('displaybtn').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'cbbutton')))
            displayed = browser.find_element_by_id('cbbutton').text
            browser.get_screenshot_as_file(fname)
            with open(fname, 'rb') as data:
                s3.upload_fileobj(data, s3_output_bucket, s3_path_prefix + '/' + fname)
            os.remove(fname)
            print('Completed test %s' % funcname())
            if todisplay == displayed:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Passed', ' ')
            else:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Didn\'t find the expected text to be displayed.')
                return -1
        except:
            print('Failed while running test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Failed', traceback.print_exc())
            return -1
    
    
    def tc003(browser, mod, tc):
        # Testcase to validate whether reset button is working properly
        fname = mod + '-' + tc + '.png'
        starttime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
        endtime = ' '
        try:
            update_status(mod, tc, starttime, endtime, 'Started', ' ')
            browser.get(main_url)
            assert 'AWS CodeBuild automation' in browser.title
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'kp')))
            browser.find_element_by_xpath("//*[@id='bc']/a").click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'displaybtn')))
            assert 'AWS CodeBuild automation - Button Click.' in browser.title
            browser.find_element_by_id('displaybtn').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'cbbutton')))
            displayed = browser.find_element_by_id('cbbutton').text
            browser.find_element_by_id('resetbtn').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'cbbutton')))
            displayed = browser.find_element_by_id('cbbutton').text
            browser.get_screenshot_as_file(fname)
            with open(fname, 'rb') as data:
                s3.upload_fileobj(data, s3_output_bucket, s3_path_prefix + '/' + fname)
            os.remove(fname)
            print('Completed test %s' % funcname())
            if displayed:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Text was not reset as expected.')
                return -1
            else:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Passed', ' ')
        except:
            print('Failed while running test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Failed', traceback.print_exc())
            return -1
    
    
    def tc004(browser, mod, tc):
        # Testcase to validate whether check box is working properly
        fname = mod + '-' + tc + '.png'
        starttime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
        endtime = ' '
        try:
            update_status(mod, tc, starttime, endtime, 'Started', ' ')
            browser.get(main_url)
            assert 'AWS CodeBuild automation' in browser.title
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'kp')))
            browser.find_element_by_xpath("//*[@id='cb']/a").click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'box3')))
            assert 'AWS CodeBuild automation - Check Box.' in browser.title
            browser.find_element_by_id('box1').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'cbbox1')))
            displayed = browser.find_element_by_id('cbbox1').text
            if displayed != 'Checkbox 1 checked.':
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Checkbox1 text was not displayed.')
                return -1
            browser.find_element_by_id('box2').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'cbbox2')))
            displayed = browser.find_element_by_id('cbbox2').text
            if displayed != 'Checkbox 2 checked.':
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Checkbox2 text was not displayed.')
                return -1
            browser.find_element_by_id('box1').click()
            WebDriverWait(browser, 20).until_not(EC.visibility_of_element_located((By.ID, 'cbbox1')))
            displayed = browser.find_element_by_id('cbbox1').text
            if displayed:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Checkbox1 text was displayed after unchecking.')
                return -1
            browser.get_screenshot_as_file(fname)
            with open(fname, 'rb') as data:
                s3.upload_fileobj(data, s3_output_bucket, s3_path_prefix + '/' + fname)
            os.remove(fname)
            print('Completed test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Passed', ' ')
        except:
            print('Failed while running test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Failed', traceback.print_exc())
            return -1
    
    
    def tc005(browser, mod, tc):
        # Testcase to validate whether dropdown is working properly
        fname = mod + '-' + tc + '.png'
        starttime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
        endtime = ' '
        try:
            update_status(mod, tc, starttime, endtime, 'Started', ' ')
            browser.get(main_url)
            assert 'AWS CodeBuild automation' in browser.title
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'kp')))
            browser.find_element_by_xpath("//*[@id='dd']/a").click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.NAME, 'cbdropdown')))
            assert 'AWS CodeBuild automation - Dropdown.' in browser.title
            browser.find_element_by_id('CP').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'dvidrop')))
            displayed = browser.find_element_by_id('dvidrop').text
            cp_text = 'AWS CodePipeline is a continuous integration and continuous delivery service ' + \
                      'for fast and reliable application and infrastructure updates.'
            if displayed != cp_text:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Expected text for CodePipeline' +
                              'from dropdown was not displayed.')
                return -1
            browser.find_element_by_id('CC').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'dvidrop')))
            displayed = browser.find_element_by_id('dvidrop').text
            cc_text = 'AWS CodeCommit is a fully-managed source control service that makes it easy for ' + \
                      'companies to host secure and highly scalable private Git repositories.'
            if displayed != cc_text:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Expected text for CodeCommit' +
                              'from dropdown was not displayed.')
                return -1
            browser.find_element_by_id('CB').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'dvidrop')))
            displayed = browser.find_element_by_id('dvidrop').text
            cb_text = 'AWS CodeBuild is a fully managed build service that compiles source code, ' + \
                      'runs tests, and produces software packages that are ready to deploy.'
            if displayed != cb_text:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Expected text for CodeBuild' +
                              'from dropdown was not displayed.')
                return -1
            browser.find_element_by_id('CD').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'dvidrop')))
            displayed = browser.find_element_by_id('dvidrop').text
            cd_text = 'AWS CodeDeploy is a service that automates code deployments to any instance, ' + \
                      'including Amazon EC2 instances and instances running on-premises.'
            if displayed != cd_text:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Expected text for CodeDeploy' +
                              'from dropdown was not displayed.')
                return -1
            browser.find_element_by_id('CS').click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'dvidrop')))
            displayed = browser.find_element_by_id('dvidrop').text
            cs_text = 'AWS CodeStar enables you to quickly develop, build, and deploy applications on AWS. ' + \
                      'AWS CodeStar provides a unified user interface, enabling you to easily manage your ' + \
                      'software development activities in one place.'
            if displayed != cs_text:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Expected text for CodeStar' +
                              'from dropdown was not displayed.')
                return -1
            browser.find_element_by_id('emp').click()
            # WebDriverWait(browser, 120).until(EC.visibility_of_element_located((By.ID, 'dvidrop')))
            displayed = browser.find_element_by_id('dvidrop').text
            if displayed:
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Expected no text')
                return -1
            browser.get_screenshot_as_file(fname)
            with open(fname, 'rb') as data:
                s3.upload_fileobj(data, s3_output_bucket, s3_path_prefix + '/' + fname)
            os.remove(fname)
            print('Completed test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Passed', ' ')
        except:
            print('Failed while running test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Failed', traceback.print_exc())
            return -1
    
    
    def tc006(browser, mod, tc):
        # Testcase to validate whether images page is working properly
        fname = mod + '-' + tc + '.png'
        starttime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
        endtime = ' '
        try:
            update_status(mod, tc, starttime, endtime, 'Started', ' ')
            browser.get(main_url)
            assert 'AWS CodeBuild automation' in browser.title
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'kp')))
            browser.find_element_by_xpath("//*[@id='img']/a").click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'image1')))
            assert 'AWS CodeBuild automation - Images.' in browser.title
            image_list = browser.find_elements_by_tag_name('img')
            for image in image_list:
                imageurl = image.get_attribute('src')
                imgfile = imageurl.split('/')[-1]
                img_res = requests.head(imageurl)
                if img_res.status_code != 200 and (img_res.status_code == 403 and imgfile != 'test3.png'):
                    endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                    update_status(mod, tc, starttime, endtime, 'Failed', 'Expected images not displayed.')
                    return -1
            browser.get_screenshot_as_file(fname)
            with open(fname, 'rb') as data:
                s3.upload_fileobj(data, s3_output_bucket, s3_path_prefix + '/' + fname)
            os.remove(fname)
            print('Completed test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Passed', ' ')
        except:
            print('Failed while running test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Failed', traceback.print_exc())
            return -1
    
    
    def tc007(browser, mod, tc):
        # Testcase to validate whether keypress page is working properly
        key_pos = [Keys.ALT, Keys.CONTROL, Keys.DOWN, Keys.ESCAPE, Keys.F1, Keys.F10, Keys.F11, Keys.F12, Keys.F2,
                   Keys.F3, Keys.F4, Keys.F5, Keys.F6, Keys.F7, Keys.F8, Keys.F9, Keys.LEFT, Keys.SHIFT, Keys.SPACE,
                   Keys.TAB, Keys.UP]
        key_word = ['ALT', 'CONTROL', 'DOWN', 'ESCAPE', 'F1', 'F10', 'F11', 'F12', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7',
                    'F8', 'F9', 'LEFT', 'SHIFT', 'SPACE', 'TAB', 'UP']
        fname = mod + '-' + tc + '.png'
        starttime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
        endtime = ' '
        if br == 'firefox':
            print('Skipping this test for FireFox due to gecko driver issue.')
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Skipped', 'Skipping this test for FireFox due' +
                          'to gecko driver issue.')
            return 0
        try:
            update_status(mod, tc, starttime, endtime, 'Started', ' ')
            browser.get(main_url)
            assert 'AWS CodeBuild automation' in browser.title
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'kp')))
            browser.find_element_by_xpath("//*[@id='kp']/a").click()
            WebDriverWait(browser, 20).until(EC.visibility_of_element_located((By.ID, 'titletext')))
            assert 'AWS CodeBuild automation - Key Press.' in browser.title
            actions = webdriver.ActionChains(browser)
            actions.move_to_element(browser.find_element_by_id('titletext'))
            actions.click()
            rnum = random.randrange(0, 20)
            actions.send_keys(key_pos[rnum])
            actions.perform()
            WebDriverWait(browser, 5).until(EC.visibility_of_element_located((By.ID, 'keytext')))
            displayed = browser.find_element_by_id('keytext').text
            browser.get_screenshot_as_file(fname)
            with open(fname, 'rb') as data:
                s3.upload_fileobj(data, s3_output_bucket, s3_path_prefix + '/' + fname)
            os.remove(fname)
            if displayed != 'You pressed \'' + key_word[rnum] + '\' key.':
                endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
                update_status(mod, tc, starttime, endtime, 'Failed', 'Expected key press not displayed.')
                return -1
            print('Completed test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Passed', ' ')
        except:
            print('Failed while running test %s' % funcname())
            endtime = datetime.strftime(datetime.today(), '%d-%m-%Y %H:%M:%S,%f')
            update_status(mod, tc, starttime, endtime, 'Failed', traceback.print_exc())
            return -1
    
    
    def test_phantomjs():
        phantomjs_function = os.environ['PhantomJSFunction']
        mods = os.environ['MODULES'].split(',')
        client = boto3.client('lambda')
        for mod in mods:
            mod_tcs = ddb.get_item(TableName=module_table, Key={'module': {'S': mod.strip()}})['Item']['testcases']['L']
            for tc in mod_tcs:
                tcname = str(tc['S'].strip())
                try:
                    response = client.invoke(ClientContext=tcname, FunctionName=phantomjs_function, InvocationType='Event',
                                             Payload="{\"testcase\": \"" + tcname + "\", \"module\": \"" +
                                             mod.strip() + "\"}")
                    print(response)
                except:
                    print('Failed while invoking test %s' % tcname)
                    traceback.print_exc()
                    print(traceback.print_exc())
    
    
    if __name__ == '__main__':
        if br.lower() == 'chrome':
            browser = webdriver.Chrome()
        elif br.lower() == 'firefox':
            browser = webdriver.Firefox()
        elif br.lower() == 'phantomjs':
            test_phantomjs()
            sys.exit(0)
        else:
            print('Unexpected browser value: %s', br)
            sys.exit(-1)
        mods = os.environ['MODULES'].split(',')
        allmthd = globals().copy()
        allmthd.update(locals())
        for mod in mods:
            mod_tcs = ddb.get_item(TableName=module_table, Key={'module': {'S': mod.strip()}})['Item']['testcases']['L']
            for tc in mod_tcs:
                tcname = str(tc['S'].strip())
                method = allmthd.get(tcname)
                if not method:
                    raise Exception("Test case %s is not implemented" % tc['S'].strip())
                method(browser, mod.strip(), tcname)
    
        if browser:
            browser.quit()
    

    終わりに

    2年前と少し古いものでしたので、今ならもうちょっと実装が変わるのではという気はします。

    ですが、例えばCloudWatch EventからLambdaを定期実行して、結果をDynamoDBに保存し、ステータスページに出す。という使い方や、Cucumber系のBDDツールで、featureをS3やDynamoDBから取得する形でテストケースを動的に変更できるようにするやりかたなども考えられるかなと思います。

    Node.js使いとしては、puppeteerを使ってlighthouseなども実行するという方法も考えてみたいなと思いました。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark