astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

特許とその制度について 国際出願

お久しぶりです。主に特許関連のデータ処理を担当しているBTと申します。 前回は、日本国に出願する際に申請できる特例と特殊な出願についてご説明いたしました。 これらは日本国において特許を取得しようとする場合に行う特殊な出願や利用できる特例ですが、これらとは別に国際出願という仕組みを利用することも可能です。 今回はこの国際出願についてご説明したいと思います。 宜しくお願いいたします。

国際出願制度とその目的

昔は日本国内でのみビジネスをしていれば十分という時代もありましたが、現在は一企業が様々な国でビジネスを行うのが当たり前なグローバル経済の時代です。 このため、特許も日本国内でのみで取得するのでは無く、現在ビジネスを行っているあるいは今後ビジネスを行う予定のある国においても通用するように特許を取得することが重要です。 しかし、特許制度やその制度ののもと付与される特許の権利は各国で独立したものであるため、それぞれの国において別個に特許を取得することが必要となります。

例えば、ある技術についてはA国では特許を取得できるが、B国ではすでに他者に特許がなされていて取得できないといったことが起こりえます。 また国によって特許として認められない技術の種類に違いがあったり、審査の厳しさにも国毎に違いがあります。 このように、特許の権利を付与するという部分については各国で明確に違いが生じてきますが、どの国においてもまずは出願をしなければならないという点では同じです。

一方で、特許の出願は何よりも早く出願することが重要です。 同じ技術であれば先に出願人に特許が付与され、他者より一日あるいは1時間でも出願が遅ければ特許は付与されません。

以上のような状況で、特許を取得しようとする国全てに対して個別に出願を行わなければならないとすると、出願人に多大な負担が掛かることになり費用も膨大なものとなるでしょう。 そこで、一回の出願で様々な国に同時に出願したことになるような制度が考えられました。 それが、特許協力条約(以下、PCT)という国際条約に基づく出願制度です。 PCTに基づく国際出願(以下、国際出願)では、PCTに加盟する全ての国(PCTの加盟国のうち一部の国のみ選択することも可能)に出願したのと同じ効果を持ちます。 これにより、出願人に掛かる負担を大幅に減らし、各国に個別に出願するよりも費用を抑えることができるようになります。 また、PCTでは出願後に出願人をサポートする仕組みも設けられていますので、これを有効に利用することでさらに出願人の負担と費用の削減を行うことが可能となります。

なお、PCTに加盟する国は現在150カ国以上となっていますので大抵の国は含まれていますが、PCT加盟国でない国において特許を取得したい場合には、国際出願とは別にその国の特許庁に対して個別に出願することが必要となりますので注意が必要です。

国際出願に必要な書類と出願先

国際出願には以下の書類が必要です。

  • 願書(PCT加盟国のうち特許を取得したい国を指定する欄があり、国を選択するか全指定を選択する)
  • 明細書
  • 請求の範囲
  • 必要な図面
  • 要約

また上記の書類に加えて、手数料が必要になります。 国際出願は、出願人のうち少なくとも1人が日本国民又は居住者であれば、その出願は日本国特許庁に出願することができます。また、WIPO国際事務局は、すべてのPCT加盟国の国民又は居住者からのPCT国際出願を受け付けています。

国際調査及び補充国際調査

国際出願されたものについては、国際調査機関(いずれかの国の特許庁や地域特許庁)により国際調査が行われます。 そのの目的は、PCT国際出願の請求の範囲に記載された発明に「関連のある先行技術」を発見することです*。 出願人には国際調査報告が送付されますが、関連があると認められた先行技術又は関連技術が記載された文献のリスト、発明の分類(国際特許分類)、調査を行った技術分野、発明の単一性の欠如に関する情報などが記載されます。 さらに、国際調査機関は、PCT国際出願の請求の範囲に記載された発明が特許性(新規性、進歩性、産業上の利用可能性)を有するものと認められるかどうかの審査官の見解を示した国際調査機関の見解書を作成し、出願人に送付します。 国際調査機関の見解書に示された特許性についての見解は、予備的かつ非拘束的なものであるため、各国での審査に影響を与えるものではありませんが、その後の手続係属、特に国内移行手続(後で説明します)をするか否かの判断のための参考情報となります。

上記の国際調査(主調査)に加えて、出願人の任意の請求により、別の国際調査機関による国際調査を提供する補充国際調査もできます。ただし、補充国際調査を行う補充国際調査機関はオーストリア特許庁(AT)、欧州特許庁(EP)、フィンランド特許庁(FI)、連邦知的財産権特許商標庁(ロシア特許庁:RU)、スウェーデン特許登録庁(SE)、シンガポール知的所有権庁(SG)、ウクライナ国家知的所有権庁(UA)、北欧特許庁(XN)、ヴィシェグラード特許機構(XV)などに限られます。 補充国際調査の目的は、複数の国際調査機関に調査を依頼することによって、先行技術を極力把握し、国内移行した後に新たな先行技術文献が発見される可能性を減少させることです。

国際公開

国際出願されたものについては、優先日(出願時に優先権の主張が伴っていれば元の出願の日、それ以外は国際出願をした日)から18ヶ月を経過した後(出願人が早期公開を希望することも可能)、速やかにWIPO国際事務局によって国際公開されます。 国際公開は、「日本語、アラビア語、中国語、英語、フランス語、ドイツ語、ロシア語、スペイン語、韓国語、ポルトガル語」(公開言語)のいずれかで出願された国際出願についてはその言語で公開されます。それ以外の言語で出願された国際出願は出願人が翻訳した公開言語のひとつの言語で行われます。 ただし、発明の名称、要約書、国際調査報告については、国際出願が英語以外の言語であっても、常に英語の翻訳が添付されます。

この国際公開は、WIPO国際事務局が、国際出願等の内容を世界に公表する意味で行われ、第三者に対して技術情報を提供する役割を果たしています。 国際公開は、すでに出願されている発明を国際的に公表する効果のほか、各指定国においてはその国が国内で未審査の出願を国内公開したときと同等の効果が発生し、先行技術文献としても利用されます。

国際予備審査

国際予備審査は、国際出願された発明の特許性に関する見解を国際調査機関の見解書に加えて入手したいとき、あるいは国際出願の内容を補正したい場合等に、出願人が任意で請求する手続です。 発明の特許性に関しては、国際調査機関がその見解書をすでに作成しています。 したがって、多くの場合、特許性判断の材料としては国際調査機関の見解書で十分なため、近年では予備審査請求の件数は減少し続けています。

しかし、その一方で国際予備審査を請求することによって明細書・図面・請求の範囲の補正が可能になるために、その補正の機会を利用したい、あるいは補正後の国際出願で改めて特許性を判断してほしいなどの理由で国際予備審査を請求する出願人も多く存在します。

国際予備審査を請求する場合、出願人は、国際調査報告と国際調査機関の見解書が出願人に送付された日から3 ヶ月又は優先日から22 ヶ月のうち、どちらか遅く満了する日までに国際予備審査の請求書を提出し、手数料の支払をする必要があります。

国内移行手続

国際出願したものにについて、上記に記載したような段階を経て、指定国のうち権利を取得したい国において審査を受けるためには、それらの国々へ国内移行手続を行う必要があります。 国内移行手続は、条約で決められた期間内(国内移行期限:通常は優先日から30ヶ月以内)に行われなければならないため注意が必要です。

指定国のうち権利を取得したい国の特許庁に対して以下のような手続を行うことが、その国に対する国内移行手続となります(指定国のうち権利を取得したい国が複数ある場合は、それぞれの国で行う必要があります)。

  • 国際出願が作成された言語が、指定官庁が認める言語でない場合、国際出願を認められた言語へ翻訳した「翻訳文の提出」
  • 指定国の国内法令が定める場合、「国内手数料の支払い」
  • 指定国が、国際出願の写しをWIPO 国際事務局から未だ入手していない場合には、「国際出願の写しの提出」(日本も含め、実際に要求する国は少なくなっている)

出願人が所定の期間内に国内移行手続を行わない場合、国内移行がなかった指定国に関しては国際出願としての効果を失い、その国の「国内出願」を取り下げたことと同様の結果(多くの場合、その国際出願は取り下げられたものとみなされる)となります。

国際出願が国内に移行した後は、国内移行した指定国の「国内出願」と同等の取り扱いとなります。 したがって、その後の手続はそれらの国が定める国内法令に従って手続を行います。 なかでも、ほとんどの指定国における特許庁が、在外者が特許庁に対して直接手続することを制限しているため、国内の代理人を立てた上で国内手続を遂行することを国内法令で求めていることに注意が必要です。

まとめ

以上、国際出願について説明をしてきました。次回は、出願公開や審査について見ていきたいと思います。

最後になりましたが、 アスタミューゼでは現在、エンジニア・デザイナーを募集中です。 興味のある方はぜひ 採用サイト からご応募ください。

参考にした資料など

BackstopJSではじめるビジュアルリグレッションテスト

f:id:astamuse:20170829184049p:plain こんにちは。デザイン部でフロントエンドエンジニアをしているkitoです。
今回は、BackstopJSを使ったビジュアルリグレッションテストについて書きたいと思います。
ビジュアルリグレッションテストとは視覚的な回帰テストのことで、具体的にはスクリーンショットを撮影して差分抽出して行うテストです。
近年のWebフロントエンド開発では、SassやWebpackのような開発環境が整うに従ってスタイルシートをモジュール化することが増えています。 それはスタイルの汎用性を高めることに大きく貢献していますが、一方で、あるパーツのスタイル修正が想定外の場所で悪影響を及ぼしてしまう可能性をもつようになりました。 この問題に対処するために、Enduring CSSのような新しいタイプの設計手法も考えられてはいますが、既存のサービスに導入するにはかなり敷居が高いでしょう。
そこで注目したいのが、ビジュアルリグレッションテストです。自動テストでデザインの異常を検知し、モジュール化して見通しが悪くなったスタイルを管理可能にします。

BackstopJSの導入

それではビジュアルリグレッションテストツールであるBackstopJSを導入していきましょう。 手元の環境は以下です。

node -v
v8.1.3
python --version
Python 2.7.11

Pythonは3系だとbackstopjsのインストールに失敗する場合があります。

mkdir example_backstopjs
cd example_backstopjs
npm init
npm install --save-dev backstopjs

これでインストールできました。 次にbackstop.jsの設定ファイルを作成します。

backstop init

backstop.jsonとbackstop_dataというフォルダが作成されたと思います。 backstop.jsonはbackstop.jsの設定を記述します。こちらにテストサイトのurlなど様々なオプションを追加しましょう。
今回は以下のように設定します。

{
    "id": "test",
    "viewports": [{
            "label": "phone",
            "width": 320,
            "height": 480
        },
        {
            "label": "pc",
            "width": 1024,
            "height": 768
        }
    ],
    "scenarios": [{
        "label": "BackstopJS testing",
        "url": "http://localhost:5000/",
        "hideSelectors": ["iframe"],
        "removeSelectors": [],
        "selectorExpansion": true,
        "selectors": [],
        "readyEvent": null,
        "delay": 0,
        "misMatchThreshold": 0.1,
        "requireSameDimensions": true,
        "onBeforeScript": "onBefore.js",
        "onReadyScript": "onReady.js"
    }],
    "paths": {
        "bitmaps_reference": "backstop_data/bitmaps_reference",
        "bitmaps_test": "backstop_data/bitmaps_test",
        "engine_scripts": "backstop_data/engine_scripts",
        "html_report": "backstop_data/html_report",
        "ci_report": "backstop_data/ci_report"
    },
    "engineFlags": [],
    "engine": "phantomjs",
    "report": ["browser"],
    "debug": true,
    "debugWindow": true
}

viewportsを指定できるので、レスポンシブサイトなどはこちらでPCとスマホサイト両方の画面サイズを設定できます。ユーザーエージェントでPCとスマホサイトを出し分けている場合、別の設定が必要になります。(後で説明します。)  
scenariosでは、更に細かい設定ができます。labelはスクリーンショットの名前、urlはテストするサイトのurl、hideSelectorsとremoveSelectorは、それぞれ指定セレクターを非表示にするオプションですが、hideSelectorsはvisibility: hiddenになり、removeSelectorsはdisplay: noneになる違いがあります。
selectorsは、スクリーンショットを撮影するパーツを指定します。ここではbody以下すべてを撮影する設定になっています。もちろん、"#main"のように一部パーツのスクリーンショットを撮影することができます。
readyEventは指定の文字列がconsole.logに表示するまで処理を待ちます。SPAに使えそうです。
readySelectorは、指定セレクターの表示を待ってから処理に移るオプションで、遅延ロードされるコンテンツがあるサイトで使えるでしょう。delayはミリ秒単位で処理を遅延させます。misMatchThresholdは、テストがfailになる閾値を設定できます。 onBeforeScriptは、テストサイトにcookieのようなブラウザの設定を反映するJavaScriptを記述できます。上記のサンプルでは./backstop_data/engine_scripts/onBefore.jsにファイルを読み込みます。onReadyScriptは、スクリーンショットを撮影する前にUIの状態を変化させたり、ユーザーエージェントの設定するJavaScriptを記述できます。今回は、./backstop_data/engine_scripts/onReady.jsに、下記のようにユーザーエージェントを変更するコードを書いてください。

module.exports = function(casper, scenario, vp) {
    if (vp.label === 'phone') {
        casper.userAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13F69 Safari/601.1');
    } else {
        casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 ');
    }
    casper.thenOpen(scenario.url);
}

casperというのは、Casper.jsというE2Eテストライブラリーのことです。ここではCasper.jsを使って、ユーザーエージェントを設定しています。Casper.jsはBacksotp.jsが依存しているライブラリーなので、npm installした際に一緒にインストールされているはずです。

ビジュアルリグレッションテスト

では簡易的なローカルサーバーを立ててテストしてみましょう。 node.jsのserveモジュールをインストールします。さらにbootstrapも入れましょう。

npm install -g serve
npm install --save-dev bootstrap

index.htmlを下記のように作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Backsotp.js testing</title>
    <link href="./node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <div class="jumbotron">
            <h1>example</h1>
            <p>
                <a class="btn btn-lg btn-primary" href="#" role="button">button</a>
            </p>
        </div>
    </div>
    <script src="./node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
</body>
</html>

ターミナルで別タブを開いて、example_backstopjsのディレクトリまで移動します。「serve」で簡易サーバーが立ち上がるのでhttp://localhost:5000/にアクセスしてみましょう。

serve

下のように表示されていると思います。 f:id:astamuse:20170829183825p:plain

準備が整ったので実際にテストしてみましょう。ターミナルで下記コマンドを入力してください。 すると./backstop_data/bitmaps_reference/以下に画像が2枚撮影されていると思います。 これがテストの元になるreference画像になります。

./node_modules/.bin/backstop reference

次に下記を実行してください。

./node_modules/.bin/backstop test

ブラウザーが立ち上がって下記のようなhtmlが表示されるます。これはbackstop.jsonのreportに"browser"を指定しているからです。 f:id:astamuse:20170829183835p:plain 全く何も変更していないので、テストは成功していると思います。 そして次にに、index.htmlのテキスト「button」を「button2」などに変更してわざと失敗させてみましょう。 failになり下記のようなスクリーンショットが表示されて、差分が着色されて表示されます。

f:id:astamuse:20170829183839p:plain

html上のshow statsのチェックを入れると、右側に下記のようなREPORTが表示されます。  misMatchPercentageの値をみると、0.39%ミスマッチであることがわかります。

Report: {
  "isSameDimensions": true,
  "dimensionDifference": {
    "width": 0,
    "height": 0
  },
  "misMatchPercentage": "0.39",
  "analysisTime": 49,
  "getDiffImage": null
}
Threshold: 1

engineをphantomjsではなくて、chromeに変更することも可能です。 実際のサービスサイトに使うにはmisMatchThresholdの値や除外セレクターの設定を細かく検討する必要があるでしょう。

アスタミューゼでは、エンジニア・デザイナーを募集中です。ご興味のある方は遠慮なく採用サイトからご応募ください。お待ちしています。

TypescriptでJavascriptの這い寄る混沌からなんとか抜け出した話

f:id:astamuse:20170823011437j:plain

お久しぶりでございます。scalaでバックエンドを開発しているaxtstar(@axtstart)でございます。

前回はマクロって言ったり、関数って言ったり、Functionだったりする何か。~Google Spread Sheet~ の話をしましたが、今回は、Typescriptを使って、Javascriptで書かれたテストコードを効率化した話を書いてみようと思います。

TL;DR

Typescriptとデザインパターンの「テンプレート・メソッド・パターン」が分かる方はこの記事は読まずに、
「エンジニア・デザイナーの採用情報」のバナーからぜひ弊社にご応募ください。

テストは重要

プロダクションをデプロイする前に、テストを行うのが当然なのはご存知の通りですが、 私の担当してるプロジェクトでは、導通確認やトランザクションチェックにヘッドレスブラウザ*1を用いた試験を導入しています。

デプロイした後のプロダクションに対してもテストを実行しシステムの健全動作を定期的に確認しています。

↓こんなテスト↓

STEP1 → STEP2 → STEP3 → STEP4

STEPは各ページに相当

→は画面遷移

PhantomJS

担当プロジェクトでは経緯があって、PhantomJSを上記テストに利用しています。

PhantomJSは、

  • Javascript(ES5)互換であること
  • ヘッドレスブラウザ(Qt web kitを使用)であること
  • 画面キャプチャ機能があること
  • NodeJS上で動くわけではないこと*2
  • 残念ながら主要メンテナーの方が最近ステップダウンされた*3

などの特徴があり、よく知られたヘッドレステストツールです。

問題点

しかしながら、Javascriptで記述していたためというわけでは無いのですが、 以下の問題を抱えている状態でした。

  • 可読性が低い(あくまで処理の順番通りにコードが書かれているだけ)
  • 構造化されていない(処理が最初から終わりまで1メソッドで書かれていたりする)
  • 似て非なるテストが多くあり、メンテナンスに時間を要する
  • あるテストではAssertがあるのに他のものではなかったりする(テスト品質にバラツキがある)

這い寄る混沌

そんな中、テスト全てに影響のある修正が行われることになりました。

修正そのものは(それほど)難しくは無いのですが、それが動作する動線が多く、このためのテスト対応を、 現状のソースに対して行うのはSAN値が下がる大変だと直感しました。

↓ちなみにこのような修正↓

STEP1 → STEP2 → STEP3 → STEP4

STEP1 → STEP1-2 → STEP2 → STEP3 → STEP4

そこで今回このコードを一気にモダン化しようと思い至りました。

AltJS導入

かつては、CoffeeScriptのソースが直接読み込めたPhantomJSですが、version 2以降はメンテナンスの関係からその対応は外されています。*4

ですので、モダン化を行うには、現状、AltJSと呼ばれる、対象の言語を、PhantomJSが動作するJavascriptにトランスパイルする言語・環境が必要になってきます。

こちらのエントリでBabel、ES6の話を書いているので初めはBabelによるES5へのトランスパイルを試そうかな*5と思っていました。

しかし、@typesの記事を目にして、Typecriptを採用してみることにしました。

※これ以前はtsdとかTypingsとかNodeJSの設定以外にも色々手を加える部分が必要なTypescriptはとっつきにくいなと感じていました。

修正前

さて、

「そのもの」のソースはお見せできないので、今回、大幅に簡略、概念化した、修正前のソースを用意しました。

Webページの遷移において、ユーザが好きな何かを選択して画面遷移するもの、としています。

※そうは言っても、あくまで今回のブログ用に作成したものなので、「そのもの」よりかなり可読性は高いです。

phantom.casperPath = './node_modules/casperjs';
phantom.injectJs('./node_modules/casperjs/bin/bootstrap.js');
phantom.casperTest = true;

var casper = require('casper').create();
var fs = require('fs');

var url = "http://sample.com/";
var wait_time= 300;
var title;

casper.test.begin('test start', function suite(test) {

    //UserAgent指定
    casper.userAgent('Mozilla/5.0 (Macintosh略');

    casper.start().viewport(1024, 768).thenOpen(url, function() {
        test.assertTitle("タイトル", "title is タイトル");
        title = this.getTitle();
    });

    casper.thenEvaluate(function() {
            document.querySelector('input[name="ログインID"]').value = 'ログインID';
            document.querySelector('input[name="パスワード"]').value = 'パスワード';
            document.querySelector('#clickable').click();
        }, '')
        // STEP 1
        .then(function() {
            this.test.assertTitleMatch(/STEP1/, "title contains STEP1");
            this.wait(wait_time, function() {
                this.fillSelectors('form', {
                    'select[name="好きな映画"]': 'マッドマックス 怒りのデス・ロード',
                    'select[name="好きな車"]': 'Peugeot 106'
                }, false);

                this.click('#clickable');//Next Page
            });
        })
        // STEP 2
        .then(function() {
            this.wait(wait_time, function() {
                this.test.assertTitleMatch(/STEP2/, "title contains STEP2");

                this.fillSelectors('form', {
                    'select[name="好きなチーム"]': '阪神タイガース',
                    'select[name="好きな選手"]': 'ジネディーヌ・ジダン'
                }, false);

                this.click('#clickable');//Next Page
            });
        })
        // STEP 3
        .then(function() {
            this.wait(wait_time, function() {
                this.test.assertTitleMatch(/STEP3/, "title contains STEP3");

                this.fillSelectors('form', {
                    'select[name="好きな国"]': 'フランス',
                    'select[name="好きな曲"]': '愛なんだ'
                }, false);

                this.click('#clickable');//Next Page
            });
        })
        // STEP 4
        .then(function() {
            this.wait(wait_time, function() {
                this.test.assertTitleMatch(/STEP4/, "title contains STEP4");

                this.fillSelectors('form', {
                    'select[name="好きな番組"]': '正解するカド',
                    'select[name="好きな本"]': 'The Three Body Problem'
                }, false);

                this.click('#clickable');//Next Page
            });
        });

  casper.run(function () {
    test.done();
  })
  .then(function(){
    this.exit();
  });

});

このようなJSファイルが数十個あるような状態でした。

このソース群一個一個個別に、同じ追加の処理を書いて行くのは、やはり遠い目になるのは間違いないので、 今見てもこの選択は良かったなと思っています。

目標

Typescript導入による目標を下記のごとく定めました。

  • 構造化による差分プログラムを可能にすること
  • Enum化によるインテリセンスの活用、値入力の効率化、ランダム取得対応

構造化

上記を基本として、必要に応じてクラスをオーバライドすることで、 各処理に対応するようにしました。

また管理したい単位でクラス設計を行いました。

  • People ユーザの属性を保持しておくクラス
  export class People{
    /** ログインID */
    public loginID:string
    /** パスワード */
    public passwd:string
    /** 好きな映画 */
    public movie:string = MovieEnum.fury
    /** 好きな車 */
    public car:string = CarEnum.peugeot106
    //以下略
  }

入力のものはstring、選択式のものはenumを使用しました(後述)

↓構造↓

基本構造として、URLを開いてログインするだけで終わる処理をBASEとして記述します。 そのフローの中で、何もしない処理を記述します

Start → Login → Step1(何もしない) → Step2(何もしない) → Step3(何もしない) → Step4(何もしない)

export module TestModule {
  export class Register {
      //開始URL
      public url = "http://sample.com"

      //人物設定
      public people:People = new OrdinalPeople()

      protected beginningString:string = "start"
      protected assertTitle:string
      protected sizeWidth:number = 1024
      protected sizeHeight = 768

       /**
        * スタート処理(は共通として扱う)
        */
       protected emitStart = () => {
          var url = `${this.url}`
          var assertTitle = this.assertTitle
          var sizeWidth = this.sizeWidth
          var sizeHeight = this.sizeHeight

          casper.start().viewport(sizeWidth, sizeHeight).thenOpen(url, function() {
              var title = this.getTitle()
              casper.test.assertTitle(assertTitle, "title is " + title)
          })
       }

       /**
        * ログイン処理(は共通として扱う)
        */
       protected emitLogin = () => {
          var loginID = this.people.loginID
          var passwd = this.people.passwd

          casper.thenEvaluate(function(loginID:any,passwd:any) {
                  (<HTMLInputElement>document.querySelector('input[name="ログインID"]')).value = loginID
                  (<HTMLInputElement>document.querySelector('input[name="パスワード"]')).value = passwd
                  (<HTMLButtonElement> document.querySelector("#clickable")).click()
              }, loginID, passwd)
       }

      /** STEP1処理 */
      protected emitStep1 = () => {}

      /** STEP2処理 */
      protected emitStep2 = () => {}

      /** STEP3処理 */
      protected emitStep3 = () => {}

      /** STEP4処理 */
      protected emitStep4 = () => {}

       /**
        * casperの実行
        */
       protected emitRun = () => {
          casper.run(function () {
              casper.test.done()
          })
          .then(function(){
              this.exit()
          })
       }

      /** 実際に呼び出す処理 */
      protected casperFunc = () => { 
        var emitStart = this.emitStart //スタート
        var emitLogin = this.emitLogin //ログイン
        var emitStep1 = this.emitStep1 //Step1入力を想定
        var emitStep2 = this.emitStep2 //Step2入力を想定
        var emitStep3 = this.emitStep3 //Step3入力を想定
        var emitStep4 = this.emitStep4 //Step4入力を想定

        var emitRun = this.emitRun //テストの実行

          casper.test.begin(this.beginningString, function suite(test:any) {
              emitStart();//スタート
              emitLogin();//ログイン
              emitStep1();//Step1入力を想定
              emitStep2();//Step2入力を想定
              emitStep3();//Step3入力を想定
              emitStep4();//Step4入力を想定

              emitRun();//テストの実行
          });
       }
  }
}

上記のクラスを継承し、Step1の処理を差分で書き換えるようにしました

Start → Login → Step1(映画、車) → Step2(何もしない) → Step3(何もしない) → Step4(何もしない)

export class MovieRegister extends Register {
    //人物設定
    people = new OrdinalPeople()

      /**
      * step1の設定JSON
      */
      getStep1 = (people:People) =>{
                      return {
                            'input[name="好きな映画"]':people.movie,
                            'input[name="好きな車"]': people.car
                        }
      }

      /**
      * step1入力時のcasper処理
      */
      emitStep1 = () => {
        var step1 = this.getStep1(this.people)
          //↑ここでクラスプロパティをvarに詰め替える
        casper.then(function() {
                this.fillSelectors('form', step1, false)
                this.click('#clickable');//Next Page
        })
    }
  }

Enum化

設定値をEnum化することで、テストコーディング時にインテリセンス(タブ補完、オートコンプリーション)による、効率的なコーディングを可能にしました。

export enum UserAgentEnum {
    /**
    * UserAgent指定PC
    */
    PC  = <any>'Mozilla/5.0 略',
    /**
    * UserAgent指定SP
    */
    SP = <any>"Mozilla /5.0 (iPhone;略"
}

f:id:astamuse:20170822221038p:plain

Stringのenum型が許容されないため、文字列の前に<any>を指定する必要がありました。

トランスパイルされたJavascriptは下記のようになっていました。

    /**
     * User Agnet指定
     */
    var UserAgentEnum;
    (function (UserAgentEnum) {
        /**
         * UserAgent指定PC
         */
        UserAgentEnum[UserAgentEnum["PC"] = 'Mozilla/5.0 略'] = "PC";
        /**
         * UserAgent指定SP
         */
        UserAgentEnum[UserAgentEnum["SP"] = "Mozilla /5.0 (iPhone;略"] = "SP";
    })(UserAgentEnum = TenCasper.UserAgentEnum || (TenCasper.UserAgentEnum = {}));

このため、例えばランダムで値を取得したい(ランダムでUserAgentを変更したい)場合サイズ取得時に2で割る必要がありました。

    export function getRandomEnum(o:any) {
        var len = Object.keys(o).length / 2// size of Object
        var x = Math.floor(Math.random() * len) // generate random value till the max size
        return <any>Object.keys(o)[x]
    }

このような形で選択式の部分をEnum化しTypescriptの特徴である、型システムを最大限利用できるようにしました。

また、Enum値のランダム取得を行うことで、網羅率を高める取り組みが比較的容易になったかなと思います。

朗報

Typescript2.4 から stringのenumがサポートされるようになりました。*6

export enum UserAgentEnum {
    /**
    * UserAgent指定PC
    */
    PC  = 'Mozilla/5.0 略',
    /**
        * UserAgent指定SP
        */
    SP = "Mozilla /5.0 (iPhone;略"
}

↓トランスパイル↓

/**
    * User Agnet指定
    */
var UserAgentEnum;
(function (UserAgentEnum) {
    /**
        * UserAgent指定PC
        */
    UserAgentEnum["PC"] = "Mozilla/5.0 \u7565";
    /**
        * UserAgent指定SP
        */
    UserAgentEnum["SP"] = "Mozilla /5.0 (iPhone;\u7565";
})(UserAgentEnum = TenCasper.UserAgentEnum || (TenCasper.UserAgentEnum = {}));

さきほどのランダムの取得もシンプルになりました。

export function getRandomStringEnum(an:any) {
    //String Enum may be just same size of definition?
    var len = Object.keys(an).length  // size of Object

    var x = Math.floor(Math.random() * len) // generate random value till the max size

    var key = <any>Object.keys(an)[x]
    target = an[key]

    return target
}

まとめ

Typescriptで プログラムができるようになり、以下のメリットがありました。

  • 型システムの恩恵により、ミスペルなどのケアレスミスの軽減
  • enumをランダムで回すテストによる、網羅率の向上
  • 動線でのキャプションの一貫性の無さ、css-selectorの命名の違いの検出
  • SAN値の低下を防ぐ

最後に

アスタミューゼでは現在、エンジニア・デザイナーを募集中です。 興味のある方はぜひ下記バナーからご応募ください。

*1:テスト用途のディスプレイを必要としないブラウザのことです

*2:そのため、NodeJSで使用可能なものが使えない、機能が違うなどがあります。またリモートデバッグ機能はありますが、ステップバイステップの実行はできないようです

*3:https://www.infoq.com/jp/news/2017/04/Phantomjs-future-uncertain

*4:https://github.com/ariya/phantomjs/issues/12410

*5:PhantomJSそのもののES6対応もversion 2.5で予定はされている模様です https://github.com/ariya/phantomjs/issues/14506

*6:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html#string-enums

Copyright © astamuse company, ltd. all rights reserved.