Webアプリのスマホテストを自動化する

Webブラウザアプリケーションのスマホテストを外部サービスを使い自動化します。
[AWS Device Farm](https://aws.amazon.com/jp/device-farm/ "")や[Remote TestKit](https://appkitbox.com/testkit/ "")等のサービスが対象になりますが、今回はAWS Device Farmを利用します。
なお、CIツールはプラグインが存在するJenkinsを用います。

## 必要なもの
+ AWS アカウント
+ [Amazon EC2 のキーペア](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-key-pairs.html "") 
+ [AWS IAM User(DeviceFarmFullAccess権限を付与されたユーザ)](https://github.com/Thirosue/devicefarm-sample/tree/master/provisioning "") 
+ DeviceFarm Project（テスト対象プロジェクト）
+ DeviceFarm DevicePool（テスト対象端末のプール）
+ [Terraform](https://www.terraform.io/ "") 

## Jenkinsのインストール

terraformを用いてEC2上に、Jenkinsをセットアップします。
スクリプトでは、ユーザデータを用いて、Jenkinsのセットアップを実施しています。

[参考レポジトリ](https://github.com/Thirosue/devicefarm-sample "") 

```:bash
$ git clone https://github.com/Thirosue/devicefarm-sample.git
$ cd devicefarm-sample/provisioning/
$ terraform apply -var-file=~/.aws/terraform.tfvars -var 'key_name=[keypair]'
```

```ec2_jenkins.tf
variable "key_name" {}

provider "aws" {
  region = "ap-northeast-1"
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners = ["amazon"]

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

  filter {
    name   = "root-device-type"
    values = ["ebs"]
  }

  filter {
    name   = "name"
    values = ["amzn-ami-hvm-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name   = "block-device-mapping.volume-type"
    values = ["gp2"]
  }
}

resource "aws_instance" "jenkins" {
  ami           = "${data.aws_ami.amazon_linux.id}"
  instance_type = "t2.micro"
  key_name      = "${var.key_name}"
  user_data = <<EOF
IyEvYmluL2Jhc2gKCndnZXQgLU8gL2V0Yy95dW0ucmVwb3MuZC9qZW5raW5zLnJl
cG8gaHR0cDovL3BrZy5qZW5raW5zLWNpLm9yZy9yZWRoYXQvamVua2lucy5yZXBv
CnJwbSAtLWltcG9ydCBodHRwOi8vcGtnLmplbmtpbnMtY2kub3JnL3JlZGhhdC9q
ZW5raW5zLWNpLm9yZy5rZXkKCnl1bSBpbnN0YWxsIC15IGdpdCBqZW5raW5zIGph
dmEtMS44LjAtb3BlbmpkawphbHRlcm5hdGl2ZXMgLS1zZXQgamF2YSAvdXNyL2xp
Yi9qdm0vanJlLTEuOC4wLW9wZW5qZGsueDg2XzY0L2Jpbi9qYXZhCgpjaGtjb25m
aWcgamVua2lucyBvbgovZXRjL2luaXQuZC9qZW5raW5zIHN0YXJ0CgpleGl0IDA=
EOF
}
```

```userdata.sh
#!/bin/bash

wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key

yum install -y git jenkins java-1.8.0-openjdk
alternatives --set java /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java

chkconfig jenkins on
/etc/init.d/jenkins start

exit 0
```

## Jenkinsの管理コンソールセットアップ

EC2のPublicIPを確認し、以下にアクセス

http://[IPv4 Public IP]:8080

アクセス後、Jenkinsがロックされているので、指示通り/var/lib/jenkins/secrets/initialAdminPasswordを確認し入力します。

![Unlock Jenkins](https://raw.githubusercontent.com/Thirosue/devicefarm-sample/master/provisioning/img/jenkins.png "")

## Device Farm Plugin　インストール

Jenkins-プラグインマネージャよりaws-device-farm-pluginをインストール。

![DeviceFarmPlugin](https://raw.githubusercontent.com/Thirosue/devicefarm-sample/master/provisioning/img/devicefarmplugin.png "")

## DeviceFarm AccessKey/SecretKey設定

Jenkinsの管理画面に用意したIAMユーザのAccessKey/SecretKeyを設定

![DeviceFarmIAMSetting](https://raw.githubusercontent.com/Thirosue/devicefarm-sample/master/provisioning/img/devicefarmiamsetting.png "")

## テストコード作成

以下を参考にテストコードを作成する。
[参考レポジトリ](https://github.com/Thirosue/devicefarm-sample "") はAppium+JUnitをgradleでbuildしている。

```SampleTest.java
import com.codeborne.selenide.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Platform;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

import java.io.File;
import java.net.URL;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class SampleTest {

    private RemoteWebDriver driver;

    @Before
    public void setUp() throws Exception {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setPlatform(Platform.IOS);
        capabilities.setBrowserName("safari");
        driver = new RemoteWebDriver(new URL("http://127.0.0.1:4723/wd/hub"),
                capabilities);
        driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
    }

    @After
    public void tearDown() throws Exception {
        driver.quit();
    }

    public boolean takeScreenshot(final String name) {
        String screenshotDirectory = System.getProperty("appium.screenshots.dir", System.getProperty("java.io.tmpdir", ""));
        System.out.println(screenshotDirectory);
        File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
        return screenshot.renameTo(new File(screenshotDirectory, String.format("%s.png", name)));
    }

    @Test
    public void runTest() throws Exception {
        driver.get("https://www.google.co.jp/");
        Thread.sleep(1000);

        System.out.println(driver.getTitle());
        System.out.println(driver.getPageSource());

        driver.findElement(By.id("lst-ib")).sendKeys("AWS DeviceFarm");
        driver.findElement(By.id("tsbb")).click();
        assertTrue(takeScreenshot("index"));

        assertEquals("AWS DeviceFarm", driver.findElement(By.id("lst-ib")).getAttribute("value"));
        assertTrue(takeScreenshot("result"));
    }
}
```

## build設定

[参考レポジトリ](https://github.com/Thirosue/devicefarm-sample "") はカスタムタスクのinstallZipでDeviceFarmへのuploadファイルを生成する。

![スクリーンショット 2017-09-14 時刻 20.28.51.png](https://qiita-image-store.s3.amazonaws.com/0/73176/c86db8dc-64fb-fdf5-a3db-a2f857583ead.png)

```gradle
task installZip(dependsOn: ["clean", "packageTests", "installDist"]) << {
    new File("build/work").mkdir()
    new File('build/zip.sh') << 'cd build/work; zip -r zip-with-dependencies.zip .'
    copy{
        from "build/install/test/lib/"
        into "build/work/dependency-jars/"
    }
    copy{
        from "build/libs/test-1.0-SNAPSHOT-tests.jar"
        into "build/work/"
    }
    "chmod 755 build/zip.sh".execute().waitFor()
    "build/zip.sh".execute().waitFor()
    copy{
        from "build/work/zip-with-dependencies.zip"
        into "build/"
    }
}
```

## DeviceFarmテスト設定

作成済みのDeviceFarm Project及びDeviceFarm Device Poolを選択して、buildで固めたzipファイルを指定する。 

![スクリーンショット 2017-09-14 時刻 20.34.19.png](https://qiita-image-store.s3.amazonaws.com/0/73176/d6f20c18-e3a7-032b-634e-77821e615335.png)

## テスト実行

Jenkinsでテスト実行後、DeviceFarmマネジメントコンソールへのレポートリンク等がJenkinsのテスト結果画面に表示されます。

![スクリーンショット 2017-09-14 時刻 20.39.01.png](https://qiita-image-store.s3.amazonaws.com/0/73176/b1a5052c-c618-18ee-9ca2-31052377bf0d.png)

## その他（開発中のテストについて）

[54.244.50.32～54.244.50.63. の IP範囲をホワイトリストに登録](https://aws.amazon.com/jp/device-farm/faq/ "") すれば、開発中資源もテストできそうです。

## 終わりに

利用事例が少なそうですが、かなり使えそうなサービスなので、今後積極的に利用していきたいと思います。