1. Jenkins

apptest.ai Integration for Jenkins

Automating Android and iOS app testing with a jenkins pipeline

This document explains how to configure Jenkins and use our APIs to automatically run apptest.ai tests from the build phase.

Please refer to the link Jenkins Setup Guide for Jenkins installations

1. Apptest.ai – Integration API

Test Execution API

  • POST https://api.apptest.ai/openapi/v2/testset
  • Authorization Basic {user_id}:{access_key}

For details, refer to Rest APIs for CI/CD page.

Example Response

{
  "data": {
    "test_count": 1, // Test Count (Integer),
    "testset_id": 192948 // Test Set ID (Integer)
  },
  "errorCode": 0,
  "reason": "",
  "result": "ok"
}

Callback result data format

The testing result in the JUnit XML Format is returned using the callback URL.

<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="TestBot Test">
    <testsuite name="TestBot Test.Apk File Name (with Version)">
        <testcase name="Device Name 1" time="Test Duration (sec)">
        </testcase>
        <testcase name="Device Name 2" time="Test Duration (sec)">
            <error message="apptest.ai Result Page Link"></error>
        </testcase>
        ...
    </testsuite>
</testsuites>

Check test status

  • GET https://api.apptest.ai/openapi/v2/testset/{testset_id}
  • Authorization Basic {user_id}:{access_key}

Get test result

  • GET https://api.apptest.ai/openapi/v2/testset/{testset_id}/result
  • Authorization Basic {user_id}:{access_key}

Get scenario ID list

  • GET https://api.apptest.ai/openapi/v2/project/{project_id}/scenarios
  • Authorization Basic {user_id}:{access_key}

2 apptest.ai - Preperation

2.1 Access Key and Project ID

To integrate apptest.ai into a Jenkins pipline, an access key and a project ID are required.

  • How to find the access key: An access key is automatically issued when you sign up with apptest.ai. You can locate it in the apptest.ai profile page

  • How to find the project ID: A project ID is assigned when you create a testing project

By default, a Sample Test Project Page is created automatically once you sign in.

Changing the preference in the Sample Test Project is not supported. However, you can change the preference for your new projects.

2.2 App Repository and Scenario

The scenario test can only use scenarios created on Stego from the apptest.ai.

In addition, to configure a scenario test CI for an integration with Jenkins, the mobile app and scenario must be registered in the apptest.ai.

How to register a mobile app

Mobile app registration is available on the Project Preference page of the apptest.ai. You can register a file or App ID (Package name, Bundle ID).

How to register a scenario

Scenario registration is available on the Project Preference page of the apptest.ai.

To register a scenario, you must first register the app to an App Repository on the Project Setting.

You can test the scenario using the ID of the registered scenario.

3. Jenkins – Webhook Step Plugin Installation

Search and install the “Webhook step” Plugin in the Jenkins dashboard: [Manage Jenkins] -> [Manage Plugins] -> [Available]

Skip this step if you are using the apptest.ai Test Stage Code2 source code that uses polling instead of webhook in the next stage.

4. Jenkins – Pipeline configuration

This section demonstrates how to connect an apptest.ai Test stage to a Jenkins pipeline item. A Jenkins pipeline item must be already created.

Please refer to the Example Link for more detail.

  • Go to the setup page and click on the Configure page.

  • On the pipeline definition page, add the apptest.ai Test Stage Code to the Script box

[apptest.ai Test Stage Code 1] – Automated Test with Polling
import groovy.json.*

node {
    def apkFile
    def accessKey
    def projectId
    def apiHost
    def runTestsetUrl
    def testStatusCheckUrl
    def osType
    def testsetInfoLists
    def testResult

    // Add to your Preparation Stage
    stage('Preparation') {
        echo "[ apptest.ai ] Current workspace : ${workspace}"
        userID = 'ci_test@apptest.ai' // [ Modify ] Your apptest.ai's account ( Login ID )
        accessKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx' // [ Modify ] Your apptest.ai account's accesskey (Check out your profile page)
        projectId = 1234 // [ Modify ] The apptest.ai's project ID where the test will be run
        apiHost = "https://api.apptest.ai"
 runTestsetUrl = "${apiHost}/openapi/v2/testset"
        osType='ANDROID' // [ Modify ] Please enter the os type of the test target. (ANDROID | iOS)

        echo "[ apptest.ai ] Preparation successfully."
    }

    // Apptest.ai Test run Stage
    stage('apptestai Test Run') {
        apkFile="/var/jenkins_home/BBC_News_v5.5.1.2.com.apk"
        // Call apptest.ai's Test run API.
        runTestset = sh(returnStdout: true, script: "curl -X POST -u '${userID}:${accessKey}' -F 'app_file=@\"${apkFile}\"' -F 'data={\"pid\": ${projectId}, \"testset_name\": \"${env.BUILD_TAG}\",\"os_type\": \"${osType}\", \"ci_type\": \"jenkins\"}' ${runTestsetUrl}").toString().trim()
        runTestsetResponse = new JsonSlurperClassic().parseText(runTestset)
        // Test run fail case
        if (runTestsetResponse.result_code != 0) {
            echo "[ apptest.ai ] Failed to run the test. ${runTestset}"
            runTestset = null
            runTestsetResponse = null
            error "FAIL"
        }

        testsetId = runTestsetResponse['data']['testset_id']
        runTestset = null
        runTestsetResponse = null

        echo "[ apptest.ai ] Test run successfully. (Testset ID : ${testsetId})"
    }

    // Apptest.ai Test status check Stage
    stage('apptestai Test Status Check') {
        completeAll = false
        testResultXml = ''

        // Repeat until all tests are complete
        while(!completeAll) {
            // Check the test status for each item in the testsetId lists
            sleep (time: 10, unit: "SECONDS")
            testStatusCheckUrl="${apiHost}/openapi/v2/testset/${testsetId}"
            getTestStatusCheck = sh( returnStdout: true, script: "curl -X GET -u '${userID}:${accessKey}' ${testStatusCheckUrl}").toString().trim()
            getTestStatusCheckResponse = new JsonSlurperClassic().parseText(getTestStatusCheck)
            // Test status check fail case
            if (getTestStatusCheckResponse.result_code != 0) {
                echo "[ apptest.ai ] Failed to get test status. ${getTestStatusCheck}"
                getTestStatusCheck = null
                getTestStatusCheckResponse = null
                error "FAIL"
            }

            testStatus = getTestStatusCheckResponse.data.testset_status.toLowerCase()
            echo "[ apptest.ai ] testset ${testsetId} complete result : ${testStatus}"

            // Get the result data when the test is complete
            if (testStatus == 'complete') {
                testResultUrl = "${apiHost}/openapi/v2/testset/${testsetId}/result"
                getTestResult = sh( returnStdout: true, script: "curl -X GET -u '${userID}:${accessKey}' '${testResultUrl}'").toString().trim()
                getTestResultResponse = new JsonSlurperClassic().parseText(getTestResult)
                // Test result data get fail case
                if (getTestResultResponse.result_code != 0) {
                    echo "[ apptest.ai ] Failed to get test result data. ${getTestResult}"
                    getTestResult = null
                    getTestResultResponse = null
                    error "FAIL"
                }

                // Merge test result xml data
                testResultXml = getTestResultResponse.data.result_xml
                echo "[ apptest.ai ] Test completed. tsid : ${testsetId}"
                getTestResult = null
                getTestResultResponse = null
                completeAll = true
            }
            getTestStatusCheck = null
            getTestStatusCheckResponse = null
        }
        echo "[ apptest.ai ] All tests are completed."
        echo "[ apptest.ai ] Test result data : ${testResultXml}"
    }

    // Write apptest.ai test result data to file Stage
    stage('Write Apptestai Test Result') {
        sh "mkdir -p tmp/"
        // Write File file:"tmp/TESTS-Apptestai.xml", text: testResultXml, encoding: "UTF-8"
        sh "echo -n '${testResultXml}' > tmp/TESTS-Apptestai.xml"
    }

    // JUnit Test Stage
    stage('jUnit Test') {
        junit 'tmp/TESTS-*.xml'
    }
}
[apptest.ai Test Stage Code 2] – Multi Scenario Test with Polling

You must have a scenario ID, source type, and os type to run a scenario test.

import groovy.json.*

node {
    def apkFile
    def accessKey
    def projectId
    def apiHost
    def getScenarioListUrl
    def runTestsetUrl
    def testStatusCheckUrl
    def scenarioQueryString
    def osType
    def scenarioIdLists
    def testsetInfoLists
    def testResult

    // Add to your Preparation Stage
    stage('Preparation') {
        echo "[ apptest.ai ] Current workspace : ${workspace}"
        userID = 'ci_test@apptest.ai' // [ Modify ] Your apptest.ai's account ( Login ID )
        accessKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx' // [ Modify ] Your apptest.ai account's accesskey (Check out your profile page)
        projectId = 1234 // [ Modify ] The apptest.ai's project ID where the test will be run
        apiHost = "https://api.apptest.ai"
        getScenarioListUrl = "${apiHost}/openapi/v2/project/${projectId}/scenarios"
        runTestsetUrl = "${apiHost}/openapi/v2/testset"
        scenarioQueryString = '' // [ Modify ] When getting a list of scenarios, enter the characters you want included in the scenario name.
        osType='ANDROID' // [ Modify ] Please enter the os type of the test target. (ANDROID | iOS)

        // Get scenario list from project (filter : OS Type , Query String )
        getScenarioList = sh(returnStdout: true, script: "curl -X GET -u '${userID}:${accessKey}' '${getScenarioListUrl}?os_type=${osType}&q=${scenarioQueryString}'").toString().trim()
        getScenarioListResponse = new JsonSlurperClassic().parseText(getScenarioList)
        echo "[ apptest.ai ] getScenarioListResponse : ${getScenarioListResponse}"
        if (getScenarioListResponse.result_code != 0) {
            echo "[ apptest.ai ] Failed to get list of scenarios. ${getScenarioList}"
            getScenarioList = null
            getScenarioListResponse = null
            error "FAIL"
        }
        scenarioIdLists = getScenarioListResponse.data.scenarios
        getScenarioList = null
        getScenarioListResponse = null

        echo "[ apptest.ai ] Preparation successfully."
    }

    // Apptest.ai Test run Stage
    stage('apptestai Test Run') {
        testsetInfoLists = []
        scenarioIdLists.each{
            scenarioId ->
            // Call apptest.ai's Test run API.
            runTestset = sh(returnStdout: true, script: "curl -X POST -u '${userID}:${accessKey}' -F 'data={\"pid\": ${projectId}, \"testset_name\": \"${env.BUILD_TAG}\", \"scenario_id\": ${scenarioId}, \"source_type\": \"file\", \"os_type\": \"${osType}\", \"ci_type\": \"jenkins\"}' ${runTestsetUrl}").toString().trim()
            runTestsetResponse = new JsonSlurperClassic().parseText(runTestset)
            // Test run fail case
            if (runTestsetResponse.result_code != 0) {
                echo "[ apptest.ai ] Failed to run the test. ${runTestset}"
                runTestset = null
                runTestsetResponse = null
                error "FAIL"
            }

            testsetId = runTestsetResponse['data']['testset_id']
            echo "[ apptest.ai ] TestSet id : ${testsetId} is ran successfully"
            testsetInfo = [:]
            testsetInfo['testset_id'] = testsetId
            testsetInfo['complete'] = false
            testsetInfoLists << testsetInfo
            runTestset = null
            runTestsetResponse = null
        }

        echo "[ apptest.ai ] Test run successfully."
    }

    // Apptest.ai Test status check Stage
    stage('apptestai Test Status Check') {
        completeAll = false
        testResultXml = ''

        // Repeat until all tests are complete
        while(!completeAll) {
            // Check the test status for each item in the testsetId lists
            for (int i = 0; i < testsetInfoLists.size(); i++) {
                waitUntil{
                    sleep (time: 10, unit: "SECONDS")
                    item = testsetInfoLists[i]
                    testsetId = item.testset_id
                    if (item.complete == false) {
                        testStatusCheckUrl="${apiHost}/openapi/v2/testset/${testsetId}"
                        getTestStatusCheck = sh( returnStdout: true, script: "curl -X GET -u '${userID}:${accessKey}' ${testStatusCheckUrl}").toString().trim()
                        getTestStatusCheckResponse = new JsonSlurperClassic().parseText(getTestStatusCheck)
                        // Test status check fail case
                        if (getTestStatusCheckResponse.result_code != 0) {
                            echo "[ apptest.ai ] Failed to get test status. ${getTestStatusCheck}"
                            getTestStatusCheck = null
                            getTestStatusCheckResponse = null
                            error "FAIL"
                        }

                        testStatus = getTestStatusCheckResponse.data.testset_status.toLowerCase()
                        echo "[ apptest.ai ] testset ${testsetId} complete result : ${testStatus}"

                        // Get the result data when the test is complete
                        if (testStatus == 'complete') {
                            testResultUrl = "${apiHost}/openapi/v2/testset/${testsetId}/result"
                            getTestResult = sh( returnStdout: true, script: "curl -X GET -u '${userID}:${accessKey}' '${testResultUrl}?data_type=bare_xml'").toString().trim()
                            getTestResultResponse = new JsonSlurperClassic().parseText(getTestResult)
                            // Test result data get fail case
                            if (getTestResultResponse.result_code != 0) {
                                echo "[ apptest.ai ] Failed to get test result data. ${getTestResult}"
                                getTestResult = null
                                getTestResultResponse = null
                                error "FAIL"
                            }

                            // Test result xml data init value setting
                            if (testResultXml.length() == 0) {
                                testResultJson = new JsonSlurperClassic().parseText(getTestResultResponse.data.result_json)
                                testResultXml = ""
                            }
                            // Merge test result xml data
                            testResultXml = testResultXml + getTestResultResponse.data.result_bare_xml
                            echo "[ apptest.ai ] Test completed. tsid : ${testsetId}"
                            getTestResult = null
                            getTestResultResponse = null
                            item['complete'] = true
                        }
                        getTestStatusCheck = null
                        getTestStatusCheckResponse = null
                    }

                    return true
                }
            }
            // Check running test item
            runningTestItem = testsetInfoLists.findAll { element -> element.complete == false }
            // Marking all tests complete when running test count is zero
            if ( runningTestItem.size() == 0 ) {
                testResultXml = testResultXml + ""
                completeAll = true
            }
        }
        echo "[ apptest.ai ] All tests are completed."
        echo "[ apptest.ai ] Test result data : ${testResultXml}"
    }

    // Write apptest.ai test result data to file Stage
    stage('Write Apptestai Test Result') {
        sh "mkdir -p tmp/"
        // Write File file:"tmp/TESTS-Apptestai.xml", text: testResultXml, encoding: "UTF-8"
        sh "echo -n '${testResultXml}' > tmp/TESTS-Apptestai.xml"
    }

    // JUnit Test Stage
    stage('jUnit Test') {
        junit 'tmp/TESTS-*.xml'
    }
}

You can change the following items in the above script.

  • userID : apptest.ai's Login ID
  • accessKey : The access key from apptest.ai
  • projectId : The project ID created in apptest.ai
    • Testing is performed with preset devices within a time limit defined in the apptest.ai project configuration.

    • Changing the preference for the Sample Test Project is not allowed, but you can change the preference in a new project.

  • osType : OS type for test target
  • apkFile : The app path (App Binary File) to be tested

Click “Build Now” in Jenkins to start the pipeline.

5. Test Results

Once the testing is complete, the testing results in the JUnit XML result format are automatically passed onto Jenkins with a callback URL. Jenkins reflects the returned testing results.

For more detailed analysis, please visit apptest.ai

  • View Testing Results in Jenkins

  • View Testing Results in apptest.ai

Last modified October 5, 2022