読者です 読者をやめる 読者になる 読者になる

AnacondaでPython(データ解析の真似事)

/IT系 /Python @機械学習

AnacondaでPython環境を作って、データ解析の真似事としてタイタニック号の乗客名簿を読み込んで、乗客の年齢のヒストグラムを表示してみる。

Anacondaのダウンロードとインストール

下記サイトから実行環境に応じたインストーラをダウンロードしインストールする。

Download Anaconda Now! | Continuum

いまPythonデファクトPython 3系だそうなので、自分はWindows版のPython 3系のインストーラをダウンロードしインストールした。

Jupyter Notebookの起動

Pythonを書いたり実行結果を表示したりするREPLにあたるのがJupyter Notebook。 Windows版のAnacondaに付属するJupyter Notebookをスタートメニューから起動すると、ログインユーザのドキュメントフォルダをカレントディレクトリとして起動する。
だが個人的にここにコードを置くのは好きではないので、自分はコードを置くフォルダをカレントディレクトリとして起動するように、コードを置くフォルダに下記のようなバッチファイル(boot-jupyter-notebook.bat)を作ってそこから起動するようにした。

%USERPROFILE%\Anaconda3\Scripts\jupyter-notebook.exe
pause

boot-jupyter-notebook.batを起動すると、コンソールに「The Jupyter Notebook is running at: http://localhost:8888/」と出力され、このアドレスを開いたブラウザが起動する。

f:id:nave_kazu:20170307171954p:plain

Jupyter Notebookの最初に表示するページはカレントディレクトリのファイル一覧なので、boot-jupyter-notebook.batが表示されている。

次は、このJupyter NotebookからPython環境を作ったり、パッケージの管理をしたり、Pythonを書いたり、実行結果を表示したりする。

実行環境を作成する

実行環境はCondaによって管理されている。
Jupyter Notebookの上に「Conda」というタブがあるのでそこをクリックして開くと、デフォルトで構築されている実行環境「root」がある。

f:id:nave_kazu:20170307172005p:plain

それをそのまま使っても良いが、せっかくなので新しい環境を作って、そこで色々試してみる。

右上の「+」をクリックして新しい実行環境を作成する。
今回は名前を「test-env-01」とする。
「Type」はPython 3系が欲しいので、「Python 3」のまま。

f:id:nave_kazu:20170307172011p:plain

「Create」をクリックする。

しばらくすると環境一覧に「test-env-01」が現れる。

f:id:nave_kazu:20170307172017p:plain

続いてその「test-env-01」をクリックして、下のパッケージ一覧をrootからtest-env-01に切り替える。

f:id:nave_kazu:20170307172025p:plain

左側の利用可能パッケージから、データ解析ライブラリの「pandas」を選択し、「→」をクリックしてインストールする。

f:id:nave_kazu:20170307172038p:plain

続いて作図ツールの「matplotlib」を選択し、「→」をクリックしてインストールする。

f:id:nave_kazu:20170307172049p:plain

これで今回使う環境「test-env-01」は完成。

続いて、実行環境での作業内容を記録するNotebookを作成する。
Notebookは作成した実行環境のREPLで、実行環境に対していくつでも作成できる。
Anacondaには複数の実行環境「Conda」がぶら下がり、「Conda」にはその実行環境で実行することを記した複数の「Notebook」がぶら下がる。
Notebookは保存が可能なので、作業途中を保存して翌日それを読み込んで再開、というようなことも出来る。

Notebookは「Files」タブの右側「New」から作成する。
プルダウンの中から先程作成した環境「test-env-01」を選択する。
プルダウンが更新されていないことがあるので、もしプルダウンに「test-env-01」がなければページの再読込を行う。

f:id:nave_kazu:20170307172055p:plain

そうすると新しいブラウザタブが開いて、test-env-01でのREPLが表示される。

f:id:nave_kazu:20170307172103p:plain

データ解析の真似事をする

kaggleからタイタニックの乗客名簿(CSV)をダウンロードする(ダウンロードにはユーザ登録が必要)。

Titanic: Machine Learning from Disaster | Kaggle

タイタニックの乗客名簿「train.csv」をダウンロードする。
ダウンロードしたtrain.csvをカレントディレクトリ(boot-jupyter-notebook.batを実行したディレクトリ)にコピーする。

まずは手始めにダウンロードしたCSVファイルを読み込んで画面に表示してみる。
Cellと呼ばれる「 In [ ] 」の部分に下記コードを貼り付ける。

import pandas as pd

data = pd.read_csv("train.csv")
data.head(3)

この簡単なコードの説明としては、まずはじめにインストールしたパッケージpandasを読み込み、そのpandasを使ってダウンロードしたtrain.csvを読み込み、先頭から3行だけを表示している。
実行はメニューから「Cell -> Run Cells」を選択するか、再生ボタンのような右向き三角のボタンをクリックする。
すると下記のような結果が得られる。

f:id:nave_kazu:20170307172110p:plain

CSVが読めたので、続いてデータ解析の真似事として、乗客の年齢のヒストグラムを表示してみようと思う。

Cellの内容を下記のように変更する。

import pandas as pd
import matplotlib.pyplot as plot

data = pd.read_csv("train.csv")
age = data.Age.fillna(-1)
plot.hist(age, range=(0, 100), bins=101)
plot.show()

今回はpandasに加えて作図ツールのmatplotlibを読み込む。
先程と同様にtrain.csvを読み込んだ後、年齢の列「Age」の欠損箇所に「-1」を埋めている。
train.csvを見てみるとわかるが、年齢の列は空欄が目立つ。空欄があるとヒストグラムを作成する際にエラーとなるため、目印として一旦「-1」を入れる。
ヒストグラム作成のためhistを呼び出し、データageと、データの範囲として0歳~100歳、表示するビンの数として101(つまり1歳ごとに1本)のヒストグラムを作成する。データの範囲を0歳~100歳としているので、欠損データに埋めた-1は無視される。
最後にヒストグラムを表示している。
すると下記のような結果が得られる。

f:id:nave_kazu:20170307175253p:plain

なるほど、乗客の分布としては、20~30代が多かったのね。
最年少は0歳で、最高齢は80歳。なるほどなるほど。

以上、ここまで。

AnacondaでPython(走り書き)

/IT系 /Python @機械学習

Anacondaとは?

Pythonを使って機械学習やらのプログラムを書く際に使う、Pythonのエコシステムを支えるプラットフォームがAnaconda。
Anacondaを使うとローカルにPythonの実行環境をいくつも構築することが出来る。

機械学習やらのプログラムを書くのに必要なライブラリをAnacondaに作成した実行環境にインストールするのだが、ライブラリの管理もAnaconda上で出来るので、実行環境Aはxxx用、実行環境Bはyyy用というように目的別に実行環境を分けるという使い方が出来る。

Continuum | Home

実行環境の管理は、Jupyter Notebookを使ってGUIで操作が可能。

Jupyter Notebookとは?

Anacondaでの実行環境の管理や、その実行環境でのREPL(Read-eval-print loop:対話型評価環境)を提供するのがJupyter Notebook。
Anacondaをインストールすると一緒に入る。

タイタニック号の生存予測

20世紀初頭に建造された豪華客船、タイタニック号が北大西洋上で氷山に接触して沈没した事故があったが、その時の乗客名簿(名前、性別、年齢、客室)と生存の情報(助かったか、助からなかったか)が残っている。
これを題材に機械学習を試すことが出来る。

Titanic: Machine Learning from Disaster | Kaggle

参考

基本、上で書いた内容は伊藤 直也さんの受け売り。
これを足がかりに学習する・・・。

Rebuild: 171: Psychologically Safe Podcast (naoya)

WEB+DB PRESS Vol.97|技術評論社
Emerging Web Technology研究室
【第22回】Jupyter Notebook+pandasによるPythonデータ解析……伊藤 直也

JavaFXで印刷

/IT系 /Java

年賀状印刷をMS Word+MS Excelの組み合わせでやっていたが、毎年印刷方法に泣かされるので簡単な自前アプリで出来ないものかと、まずは試してみようと思う。
手近な印刷用SDKとしては.NETかJavaがあったが、迷わずJavaで試す。
Javaには古くからあるAWTやJava Print Serviceなどがあるが、それらではなくJavaFX 8から追加になったJavaFX printing APIを使う。

サンプルアプリ

簡単なJavaFXのUIを使っていくつかのAPIを試して行く。

以下のようなサンプルアプリを作った。
f:id:nave_kazu:20170106185642p:plain

上のツールバーAPIを呼び出す各種ボタンと、そのツールバーの右側に部数を入力するエリア。
下の「Log area」にAPIを呼び出した結果をテキストで出力するエリア。
真ん中にアンカーペインを置いて、その中にラベルで「Hello JavaFX Print.」を表示している。
今回はこのアンカーペインを印刷する。

Scene Builderで見たHierarchyは下記の通り。
f:id:nave_kazu:20170106185651p:plain

画面の表示方法やイベント処理方法は今回の範囲外なので適当に。
まずはここまでのソースとFXMLの内容は下記の通り。

まずはFXML。
パスは「/fxml/Main.fxml」

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tools.javafx_printsample.MainController">
   <top>
      <ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
        <items>
          <Button fx:id="defaultPrinterButton" mnemonicParsing="false" onAction="#handleDefaultPrinterButtonAction" text="DefaultPrinter" />
            <Button fx:id="allPrintersButton" mnemonicParsing="false" onAction="#handleAllPrintersButtonAction" text="AllPrinters" />
            <Button fx:id="printButton" mnemonicParsing="false" onAction="#handlePrintButtonAction" text="Print" />
            <Separator orientation="VERTICAL" />
            <Label text="Copies" />
            <TextField fx:id="copiesField" alignment="CENTER_RIGHT" prefWidth="50.0" text="1" />
        </items>
      </ToolBar>
   </top>
   <bottom>
      <TextArea fx:id="logArea" prefHeight="100.0" promptText="Log area" BorderPane.alignment="CENTER" />
   </bottom>
   <center>
      <AnchorPane fx:id="printCanvas" BorderPane.alignment="CENTER">
         <children>
            <Label text="Hello JavaFX Print." />
         </children>
      </AnchorPane>
   </center>
</BorderPane>


続いてmain関数があるApp.java

package tools.javafx_printsample;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class App extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/Main.fxml"));
        loader.load();
        Parent root = loader.getRoot();
        Scene scene = new Scene(root);
        primaryStage.setTitle("Print sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

}


最後に画面のコントローラ。

package tools.javafx_printsample;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;

public class MainController {
    @FXML Button defaultPrinterButton;
    @FXML Button allPrintersButton;
    @FXML Button printButton;
    @FXML TextField copiesField;
    @FXML AnchorPane printCanvas;
    @FXML TextArea logArea;

    @FXML
    public void handleDefaultPrinterButtonAction(ActionEvent event) {
    }

    @FXML
    public void handleAllPrintersButtonAction(ActionEvent event) {
    }

    @FXML
    public void handlePrintButtonAction(ActionEvent event) {
    }

    private void addLog(String text) {
        logArea.appendText(text+"\n");
    }
}

一番下の addLog メソッドはログエリアに引数の文字列と改行を追記する "手抜き" ユーティリティメソッド。

ここまでは下準備で、以降ではこのアプリに印刷用の処理を入れて行く。

プリンタの選択

印刷をするには印刷を実行させたいプリンタを選択しないとならない。
まずはプリンタ情報の取得を試してみる。

JavaFXの印刷APIは、パッケージで言うと「javafx.print」に入っている。
このパッケージのクラスを見ると、そのものずばりな「Printer」クラスがあり、プリンタの情報はこのクラスを通じて取得できる。

Printerクラスから各種情報を取得してログエリアに出力するメソッドをMainControllerに用意。

    private void outputPrinterInformation(Printer printer) {
        addLog("*** Name:"+printer.getName());

        // ページ・レイアウトの情報を取得して出力
        PageLayout pageLayout = printer.getDefaultPageLayout();
        PageOrientation pageOrientation = pageLayout.getPageOrientation();
        addLog("    PageLayout:");
        addLog("        TopMargin:"+pageLayout.getTopMargin());
        addLog("        LeftMargin:"+pageLayout.getLeftMargin());
        addLog("        RightMargin:"+pageLayout.getRightMargin());
        addLog("        BottomMargin:"+pageLayout.getBottomMargin());
        addLog("        PrintableHeight:"+pageLayout.getPrintableHeight());
        addLog("        PrintableWidth:"+pageLayout.getPrintableWidth());
        addLog("        PageOrientation:"+pageOrientation.name());

        // 用紙の情報を取得して出力
        Paper paper = pageLayout.getPaper();
        addLog("        Paper:");
        addLog("            Name:"+paper.getName());
        addLog("            Height:"+paper.getHeight());
        addLog("            Width:"+paper.getWidth());
    }

デフォルトプリンタの取得

Windowsで言う「通常使うプリンター」を取得するには、Printerクラスのスタティックメソッド getDefaultPrinter() を使う。
このアプリを試したWindows PCのプリンタ設定は下記のようになっている。
f:id:nave_kazu:20170106185657p:plain

3台登録されていて、「通常使うプリンター」にはPDF出力用のCubePDF、あとはWindows7標準のFAXとXPS Writerという構成。

「DefaultPrinter」ボタンをクリックした時のイベントハンドラに対して、getDefaultPrinter() の結果を outputPrinterInformation() に渡す処理を記述する。

    @FXML
    public void handleDefaultPrinterButtonAction(ActionEvent event) {
        Printer defaultPrinter = Printer.getDefaultPrinter();
        outputPrinterInformation(defaultPrinter);
    }


アプリを起動して実行し、「DefaultPrinter」ボタンをクリックした時の実行結果は下記の通り。

*** Name:CubePDF
    PageLayout:
        TopMargin:54.0
        LeftMargin:54.0
        RightMargin:54.0
        BottomMargin:54.0
        PrintableHeight:734.0
        PrintableWidth:487.0
        PageOrientation:PORTRAIT
        Paper:
            Name:A4
            Height:842.0
            Width:595.0

プリンタの名前が「CubePDF」で、四方のマージンの情報や、用紙の情報が出力された。
現在「通常使うプリンター」に設定しているのは「CubePDF」なので、意図した結果が得られた。

ちなみに数値の単位は「ポイント」で、これはDTPの用語。
1ポイントは「1/72 インチ」なので、上のA4用紙の高さ「842.0」は
842/72で約「11.69 インチ」。1インチは25.4mmなので、mmにすると「297mm」。
WikipediaによるとA4用紙のサイズは「210mm×297mm」だそうなので、842ポイントは297mmで正しいというのが分かる。

プリンタの検索

現在インストールされているプリンタ全ての情報を取得するには、Printerクラスのスタティックメソッド getAllPrinters() を使う。

「AllPrinters」ボタンをクリックした時のイベントハンドラに対して、getAllPrinters() の結果を outputPrinterInformation() に渡す処理を記述する。

    @FXML
    public void handleAllPrintersButtonAction(ActionEvent event) {
        Collection<Printer> collection = Printer.getAllPrinters();
        collection.stream().forEach(this::outputPrinterInformation);
    }

アプリを起動して実行し、「AllPrinters」ボタンをクリックした時の実行結果は下記の通り。

*** Name:CubePDF
    PageLayout:
        TopMargin:54.0
        LeftMargin:54.0
        RightMargin:54.0
        BottomMargin:54.0
        PrintableHeight:734.0
        PrintableWidth:487.0
        PageOrientation:PORTRAIT
        Paper:
            Name:A4
            Height:842.0
            Width:595.0
*** Name:Fax
    PageLayout:
        TopMargin:54.0
        LeftMargin:54.0
        RightMargin:54.0
        BottomMargin:54.0
        PrintableHeight:684.0
        PrintableWidth:504.0
        PageOrientation:PORTRAIT
        Paper:
            Name:Letter
            Height:792.0
            Width:612.0
*** Name:Microsoft XPS Document Writer
    PageLayout:
        TopMargin:54.0
        LeftMargin:54.0
        RightMargin:54.0
        BottomMargin:54.0
        PrintableHeight:734.0
        PrintableWidth:487.0
        PageOrientation:PORTRAIT
        Paper:
            Name:A4
            Height:842.0
            Width:595.0

CubePDF、Fax、XPS Writerの3つ全ての情報が得られた。

プリンタの情報が得られたのは分かったので、実際に印刷を行なってみる。

出力方法の設定

印刷の単位は「印刷ジョブ」と呼ばれる。
javafx.print」パッケージを見ると、これまたドンピシャな「PrinterJob」クラスがある。
APIドキュメントにはご丁寧にサンプルまである。
印刷の手順は、

  • 印刷ジョブを作る
  • 印刷対象のノードを渡す
  • 印刷を終了する

の3ステップ。

ここで言うノードは、JavaFXのシーングラフのことで、印刷対象となるGUIのツリー階層のルートノードのこと。
ルートノードを渡せば、それに登録されているサブノードも一式印刷される。

今回は印刷対象にラベル「Hello JavaFX Print.」を子に持つアンカーペイン「printCanvas」を渡す。
「Print」ボタンをクリックした時のイベントハンドラに対して、APIドキュメントのサンプルを参考にprintCanvasを印刷する処理を記述する。

    @FXML
    public void handlePrintButtonAction(ActionEvent event) {
        PrinterJob job = PrinterJob.createPrinterJob();
        job.printPage(printCanvas);
        job.endJob();
    }

※エラー処理を省いているので、コピペ禁止

PrinterJob.createPrinterJob() は「通常使うプリンター」に対してジョブを投入する。
もし対象のプリンタを指定したければプリンタを指定するPrinterJob.createPrinterJob(Printer printer) があるのでそちらを実行するか、setPrinter(Printer printer) メソッドで指定をする。

この状態で実行して「Print」ボタンをクリックすると、CubePDFが起動してPDFの保存先設定が表示され、「変換」をクリックするとPDFが保存される。
f:id:nave_kazu:20170106185703p:plain

保存したPDFを表示すると下記の通り、printCanvas の内容が印刷されていることがわかる。
f:id:nave_kazu:20170106185708p:plain

(仮に)部数の設定

もうちょっといじってみよう。
PrinterJob クラスから取得出来る JobSettings クラスを使用すると、印刷ジョブの設定が出来る。
JobSettings を使って、ジョブの名前やページ範囲や色(モノクロ・カラーの選択)が変更できる。
部数を入力する欄をツールバーに設けているので、その内容を渡して部数設定をしてみる。

    @FXML
    public void handlePrintButtonAction(ActionEvent event) {
        PrinterJob job = PrinterJob.createPrinterJob();

        JobSettings jobSettings = job.getJobSettings();
        jobSettings.setCopies(Integer.parseInt(copiesField.getText()));

        job.printPage(printCanvas);
        job.endJob();
    }

※エラー処理を省いているので、コピペ禁止

部数を入力するエリアに「2」と入れて実行・・・したが、CubePDFの問題なのか分からないが2部印刷が出来ない。
実際のプリンタに出力してみたら2部出た。
何だろ?
f:id:nave_kazu:20170106185712j:plain

ページ設定ダイアログ

印刷時のページ設定を JobSettings などを通して設定も出来るが、印刷時にユーザに設定させることも出来る。
それが「ページ設定ダイアログ」で、下記のように PrinterJob に対して showPageSetupDialog() メソッドで表示させる。

    @FXML
    public void handlePrintButtonAction(ActionEvent event) {
        PrinterJob job = PrinterJob.createPrinterJob();

        JobSettings jobSettings = job.getJobSettings();
        jobSettings.setCopies(Integer.parseInt(copiesField.getText()));

        job.showPageSetupDialog(null);

        job.printPage(printCanvas);
        job.endJob();
    }

※エラー処理を省いているので、コピペ禁止

親のWindowを渡してモーダルにすることも出来るが、面倒だったのでnullを渡してモーダレスで試す。
実行すると下記のようにページ設定ダイアログが表示される。
f:id:nave_kazu:20170106185719p:plain

印刷ダイアログ

印刷対象のプリンタを選択する印刷ダイアログを選択させることも出来る。
それが「印刷ダイアログ」で、下記のように PrinterJob に対して showPrintDialog() メソッドで表示させる。

    @FXML
    public void handlePrintButtonAction(ActionEvent event) {
        PrinterJob job = PrinterJob.createPrinterJob();

        JobSettings jobSettings = job.getJobSettings();
        jobSettings.setCopies(Integer.parseInt(copiesField.getText()));

        job.showPageSetupDialog(null);
        job.showPrintDialog(null);

        job.printPage(printCanvas);
        job.endJob();
    }

※エラー処理を省いているので、コピペ禁止

実行すると下記のように印刷ダイアログが表示される。
f:id:nave_kazu:20170106185723p:plain

まとめ

ページ送りはどうするとかまだ課題はあるが、まずは入り口としてはこのような感じで。

Selenideを試した

/IT系 /Java

先日参加した JJUG CCC 2016 FALL で、うらがみさんがSelenideの発表をしていた。

これを拝聴して便利に感じたので試してみた。

スライドはこちら。
Try Selenide

Selenideとは?

SelenideはSelenum WebDriverをラップしたブラウザテストの自動化フレームワークのこと。
Selenum WebDriverは各種ブラウザのWebDriver APIを呼び出す、これも自動化フレームワーク
WebDriverW3C規格なのでプロプラではなくオープンなAPIと言える。

テスト対象のアプリからSelenum WebDriverをラップしたSelenideを呼び出し、Selenum WebDriverは各種ブラウザごとのWebDriverを呼び出すことで、ブラウザからテスト対象のアプリを実行する、というのが全体の流れ。
f:id:nave_kazu:20161207152321p:plain

各種ブラウザのWebDriverを準備する

Selenide/Selenum WebDriverを使うには、まずテスト対象の各種ブラウザのWebDriverを仕込まないとならない。

Selenum WebDriverのサイトに各種ブラウザのWebDriverダウンロード先が記載してあるので、そこから目的のブラウザのWebDriverをダウンロードする。
Selenum WebDriver - Third Party Browser Drivers

ChromeならGoogle Chrome Driver
FirefoxならMozilla GeckoDriver
Internet ExplorerはSelenumが提供しているらしく、Selenum WebDriver - Downloadsの「The Internet Explorer Driver Server」にある。
EdgeからはMicrosoftが提供しているらしく、MicrosoftのサイトWebDriver - Microsoft Edge Development にある。

ダウンロードしたWebDriverは展開して適当なフォルダに保存する。
今回試したのは32bit Windows環境なので、上のサイトから32bit Windows用のChromeFirefoxInternet ExplorerのWebDriverをダウンロードして「C:\SeleniumDriver」に保存した。

ビルドスクリプトの準備と依存関係の追加

ビルドスクリプトを用意し、Selenideを含む関連するライブラリを依存関係に記述する。
関連するライブラリは、Selenide内で使用しているSLF4Jの実装と、単体テストスイートとしてJUnitになる。

Mavenのpom.xmlは下記の通り。

<dependencies>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.21</version>
  </dependency>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>com.codeborne</groupId>
    <artifactId>selenide</artifactId>
    <version>4.0</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Gradleのbuild.gradleは下記の通り。

dependencies {
  compile "org.slf4j:slf4j-simple:1.7.21"
  testCompile "junit:junit:4.12"
  testCompile "com.codeborne:selenide:4.0"
}

テストクラスの用意とSelenide/Selenum WebDriverの設定

まずJUnitから呼ばれるテストクラスを用意し、テストクラスの初期化メソッドを定義する。

public class AppTest {
    @BeforeClass
    public static void beforeClass() throws Exception {
    }
}

続いて初期化メソッドにSelenideとSelenum WebDriverの設定をする。

    @BeforeClass
    public static void beforeClass() throws Exception {
        // WebDriverのパスを設定
        System.setProperty("webdriver.ie.driver", "C:\\SeleniumDriver\\IEDriverServer.exe");
        System.setProperty("webdriver.gecko.driver", "C:\\SeleniumDriver\\geckodriver.exe");
        System.setProperty("webdriver.chrome.driver", "C:\\SeleniumDriver\\chromedriver.exe");

        // テスト結果の出力先
        Configuration.reportsFolder = "test-result/reports";

        // 使用するブラウザの指定
//        Configuration.browser = WebDriverRunner.INTERNET_EXPLORER;
//        Configuration.browser = WebDriverRunner.FIREFOX;
        Configuration.browser = WebDriverRunner.CHROME;
    }

System.setProperty()で、ダウンロードしたInternet ExplorerFirefoxChromeのWebDriverのパスを設定する。

Configuration.reportsFolderで、テスト結果(スクリーンショットなど)の保存先を指定する。
今回は相対パスで指定しているので、テスト実行時のカレントフォルダにConfiguration.reportsFolderで指定したフォルダが作成される。

Configuration.browserで、テストするブラウザを指定する。WebDriverRunner.CHROMEを指定しているので、テストを実行すると、chromedriver.exeを経由してChromeブラウザが立ち上がってテストが行われる。
Internet Explorerでテストする場合はWebDriverRunner.INTERNET_EXPLORERを指定して、Firefoxでテストする場合はWebDriverRunner.FIREFOXを指定する。
(うらがみさんは使用するブラウザを実行時引数で指定していたので、その方法はうらがみさんのスライドを参照)

テストメソッドの用意

今回は以下の手順のテストを行う。

  • http://www.yahoo.co.jp/にアクセスする
  • 検索ワードに「selenide」を入力する
  • 検索を実行する
  • 「selenide」の検索結果が出力されているか検証する

最後にスクリーンショットを保存する。

まずはテストクラスにテストメソッドを用意する。

    @Test
    public void selenideTest() throws Exception {
    }

テスト対象のメソッドに@Testを付ける。

http://www.yahoo.co.jp/にアクセスする処理を記述する。

    @Test
    public void selenideTest() throws Exception {
        open("http://www.yahoo.co.jp/");
    }

staticインポートしたopen()メソッドにアクセスするURLを指定する。

次に、検索ワードに「selenide」を入力する処理を記述する。
Yahoo!の検索ワードは下記のHTMLで表現されている。

<input name="p" id="srchtxt" type="text" value="">

なので、セレクタでID「srchtxt」を検索し、そこに値「selenide」を入れる処理を記述することになる。

    @Test
    public void selenideTest() throws Exception {
        open("http://www.yahoo.co.jp/");
        $("#srchtxt").val("selenide");
    }

jQuery風の$メソッドでDOM要素を選択して、val()メソッドで値を入れる。

さらに、検索を実行する処理を記述する。
Yahoo!の検索ボタンは下記のHTMLで表現されている。

</label><input type="submit" id="srchbtn" class="srchbtn" value="検索">

先ほどと同様にセレクタでID「srchbtn」を検索し、クリックを実行するclick()メソッドを呼び出す。

    @Test
    public void selenideTest() throws Exception {
        open("http://www.yahoo.co.jp/");
        $("#srchtxt").val("selenide");
        $("#srchbtn").click();
    }

その後、検索結果のHTMLのbody部に「selenide」の検索結果が出力されているか確認する。

    @Test
    public void selenideTest() throws Exception {
        open("http://www.yahoo.co.jp/");
        $("#srchtxt").val("selenide");
        $("#srchbtn").click();

        $("body").shouldHave(text("Selenide: concise UI tests in Java - このページを和訳"));
    }

最後にスクリーンショットを保存する。

    @Test
    public void selenideTest() throws Exception {
        open("http://www.yahoo.co.jp/");
        $("#srchtxt").val("selenide");
        $("#srchbtn").click();

        $("body").shouldHave(text("Selenide: concise UI tests in Java - このページを和訳"));
        screenshot("selenideTest");
    }

今回は相対パスで指定しているので、テスト実行時のカレントフォルダにConfiguration.reportsFolderで指定したフォルダが作成される。

テストの実行

Mavenで実行する場合、下記を実行する。

mvn test

Gradleで実行する場合、下記を実行する。

mvn test

どちらの場合でも、ブラウザが立ち上がって(今回の場合はChrome)、検索ワード「selenide」を入れて、検索して、検索結果が表示されて、ブラウザが終了してテストが正常終了すると思う。

テスト結果を保存しているはずなので、カレントフォルダの「test-result/reports」を見てみると、「selenideTest.png」と「selenideTest.html」が出来ている。
「selenideTest.png」はスクリーンショットの画像イメージで、「selenideTest.html」はその時のHTML。両方撮ってくれる。

「selenideTest.png」はこんな感じ。
f:id:nave_kazu:20161212113408p:plain

最後に

駆け足に試したが、Selenideいいね。
今後も使うと思う。

うらがみさんのスライドの中では他に、ページオブジェクトパターンやSeleniumが提供しているDockerイメージを使ったテストなども紹介している。
これらも試してみようと思う。

JJUG CCC 2016 FALLに参加してきた #jjug_ccc

/IT系 /Java

半年ごとの恒例イベントJJUG CCCの、2016年秋の会「JJUG CCC 2016 FALL」に参加してきた。
以下、自分用のメモとして参加したセッションの感想をまとめる。
f:id:nave_kazu:20161205174624p:plain

Be a great engineer!~ フォローすべきトレンド、スルーすべきトレンドをどう見抜くのか

speakerdeck.com
谷本 心(日本Javaユーザーグループ)さんの発表。
Twitterハッシュタグ#ccc_a1

浮き沈みや当たり外れの多いこのIT業界での効率的な成長の仕方が話の本質にあると思った。
技術を表面的なテクニックで捉えるのではなく、メタで捉えて「要するに何なのか」を理解することが重要とのこと。

「破壊的イノベーションと持続的イノベーション」の話で、「前に失敗したよね、これ」はたまに自分も使うから注意が必要で、再注目される理由を知ったうえで判断するのは重要だと思った。
そうだ。昔Palmイノベーションを感じて追っかけたけど廃れて、数年後にiPhoneが出たのを思い出した。Palmは失敗したけどiPhoneは大成功しているしね。つまりそういうことか。

技術カンファレンスでイノベーションの情報を手に入れるのは同意で、自分もそれが得たくて技術カンファレンスに出席している。
情報の入り口を広げるためにも英語を習得するのは個人的に必要なことだなと思う。

あとガートナー社が注目のテクノロジトレンドを発表しているらしい。
これのことかな?
ガートナーの2017年テクノロジトレンドにAIやブロックチェーン - ZDNet Japan
じっくり読んでみよう。

日本でも出来る!最先端のDevOpsを導入する方法

牛尾 剛(Microsoft Corporation)さんの発表。
Twitterハッシュタグ#ccc_c3 #ccc_cd3

「筋肉は世の中の99%の問題を解決する」とか言ったり、オープニングの「DevOps!」の合唱とか、のっけからパワフルな牛尾さん。

日本と西洋の生産性の違いについて話をしていて、カンファレンスの参加レポートをアメリカは何枚かの写真とURL貼ってメール送るだけで約2時間で完了するが、日本はプレゼン資料作ってプレゼンして・・・と約3日かかると。
なぜこのような話をしたかというと、DevOpsは西洋文化の上で成り立っているので日本文化の上でやるにはいろいろと無理が出てくるとのこと。
日本でDevOpsやるなら日本文化とDevOpsの間に西洋文化のフィルタを挟むのが良いとか。
あとDevOpsはアジャイルであることが前提。

無駄をあぶりだすのに使用するのがvalue stream mapで、ここから削れる箇所を見つけるそうな。
value stream mapはこういうもの。
情報システム用語辞典:バリューストリーム・マップ(ばりゅーすとりーむ・まっぷ) - ITmedia エンタープライズ

話の中で「エッセンシャル思考」という引用があって、それはこの本のことかな。
エッセンシャル思考 最少の時間で成果を最大にする

DevOpsスタートキットを公開しているそうなので、見てみようと思う。
DevOps スタータキットの公開 - メソッド屋のブログ

牛尾さんは「オブジェクト脳のつくり方」の著者なのか。
この本は熟読した覚えがある。

発表の最後にブログを公開していて、それがこちら。
「知らないこと」を恐れないマインドセットが技術導入を加速する - メソッド屋のブログ

GitBucketを支えるJava技術とグローバルで使われるOSSの作り方

www.slideshare.net
竹添 直樹さんの発表。
Twitterハッシュタグ#ccc_c4 #ccc_cd4

GitBucketは自分の仕事場でも使っている。
竹添さんのGitBucketの開発を通したOSS活動とはどういうことなのか?ということを知りたく聞きに行った。
まずはGitBucketの紹介。さすが竹添さん、Scala推し。

個人的にGitの技術的なところに興味があったので、GitBucketの技術スタックが見れて良かった。
GitRepositoryにアクセスするのにJGitを使っているのか、なるほど。

さまざまな拡張ポイントを作って、それをプラグインが利用するというやり方は、rebuild.fmでJenkinsのKawguchiさんが言ってたやつだ。
Rebuild: 161: Angry Jenkins Angers Me (kohsuke)
みんながやりたいことをすべて取り込めないし、他人が書いた(イケてない)コードを取り込むこともしたくないから、そういうのはプラグインで好きにやってくれという。

OSS活動で重要なのは

  • ユーザ
  • 開発者
  • コミュニティ
  • エコシステム

の4つとスライドでは書いてあったが、最も重要なのは英語。
GitBucketのUI、ドキュメント、ブログ、GitHub上でのやりとりすべてを英語でするようにしていて、それがユーザを広げるし開発者・コミュニティ・エコシステムの発展に寄与している。

やっぱ英語だよね・・・。

JavaFX アプリケーションを素敵に着飾ってみる

speakerdeck.com
トースト小僧さんの発表。
Twitterハッシュタグ#ccc_l5

JavaFXアプリの見た目を変える方法について話してくれた。
見た目はCSSで変えられて、標準のCSSテーマがあってそれを拡張・更新して見た目を変えて行く。

標準のCSSテーマは前までCaspinaと呼ばれるテーマだったが、JavaFX 8からはModenaと呼ばれるものに変わった。
ModenaのCSSは3,000行を超えるので読むのはしんどい・・・。ということで、CSSを作るツールをトースト小僧は作ったそうな。

他、テーマを変更するためのライブラリが世の中にはあるそうで、

  • JFoenix
  • RichTextFX

などが紹介された。

RichTextFXはすてきだな。
TextPaneに行番号表示やシンタックスハイライトを与えてくれるライブラリとのこと。

JavaFXはあまり注目されない技術だけど、クライアントアプリを作るのに世話になっているから、こうして盛り上げるのは良いことだと思う。
このスライドツール自体JavaFXで作られていて、なるほどドッグフードしている感があふれて好感が持てる。

受験勉強経験も留学経験もない日本人がJavaOneで英語で講演できるようになるまで

山本 裕介さんの発表。
Twitterハッシュタグ#ccc_l6

実は山本さんは帰国子女なので「そういうオチかい!」となりそうになったが、幼い時に海外生活をしていたということでノーカンだそうな。
「世界をまたにかけて仕事をする父親に憧れて」、日本の企業から外資系の会社に転職し、英語から逃れられない環境に自分を追い込んだそうで、その結果が今の姿なのだなと実感した。
「英語学習に王道はない」とのことなので、自分も愚直に出来る範囲で勉強しようと思う。

Selenideを試行錯誤しながら実践するブラウザ自動テスト

Selenideを試行錯誤しながら実践するブラウザ自動テスト
うらがみさんの発表。
Twitterハッシュタグ#ccc_c6 #ccc_cd6

SelenideはSelenium WebDriverを使いやすくラップしたフレームワークで、ブラウザテストが自動で出来るものとのこと。

序盤はSelenium WebDriverとSelenideの比較をしてくれたが、自分はSelenium WebDriverすら知らなかったので比較もさることながらテストをしている様子からして衝撃的。
比較はSelenideのGitHub Wikiにも載っているそうなので、そちらも参照。
Selenide vs Selenium ・ codeborne/selenide Wiki

Selenideを使えば各種ブラウザで所定のURLにアクセスし、フォーム送信し、結果のスクリーンショットを撮る。などのテストが容易に行える。
これは非常に便利。

うらがみさんがデモを見せてくれたが、デモも気になるがMacの壁紙の女子力の高さと「Vimを使ってくれてありがとう」も気になって仕方がなかった。

早速まずは試してみようと思う。
このセッションはテクニカルな面での今日イチの収穫だった。
あとうらがみさんがvim派だというのも収穫。

Javaエンジニアのキャリアを考えるパネルディスカッション

Jun Ohtaniさん、山本 裕介さん、Yoshiori SHOJIさん、hishidamaさんの発表。
Twitterハッシュタグ#ccc_c7 #ccc_cd7

結局は自分の道は自分で切り開くしかないのだけれど、四者四様の経歴と考え方が聞けて良かった。
よしおりさんの「周りで一番ヘタクソになるために転職」したというのは、なかなか真似できない。

「アウトプット大事」「英語大事」すごく響いた。

なのでこれからSelenideを試してアウトプットする・・・。

PlantUMLを入れてみた

/IT系

PlantUMLを使ってUMLを書いてみようと思い、まずはセットアップしたのでメモを残す。

PlantUMLとは?

PlantUMLはUMLダイアグラムを記述するためのオープンソースのツールで、他のツールとの違いはテキストファイルから図を生成するという点。
astah* UMLとか他のツールはマウスを使ってGUIで図を書くのと比べて、PlantUMLはテキストファイルに記述した定義に則って図を生成するというのが違い。
テキストファイルなのでバージョン管理もしやすいし、他のメンバーとのコラボレーションもしやすい。
反面、書き方を覚えないとならないので、イニシャルコストはかかる。

生成する図はPNG(ラスタ画像)やSVG(ベクタ画像)で生成できるので、用途に応じて使い分ける。

生成にはPlantUMLの他にGraphvizというものを使う。
PlantUMLはJavaで書かれているのでクロスプラットフォームだが、Graphvizはネイティブなので各プラットフォームに応じたバイナリを使用する。

PlantUMLのインストール

PlantUMLのダウンロードページからplantuml.jarをダウンロードして適当なフォルダに保存。

Graphvizのインストール

Graphvizのダウンロードページから適したバイナリをダウンロード。
今回はWindows版ZIPファイルをダウンロードして、releaseフォルダの中身をC:\Program Files\graphviz-2.38に展開した。
(C:\Program Files\graphviz-2.38\binに実行ファイルがあるようにした)

インストール後の動作確認

plantuml.jarが置いてあるフォルダでコマンドプロンプトを立ち上げて、以下を実行する。

java -jar plantuml.jar -testdot

PlantUMLとGraphvizがうまく動けば以下のようなメッセージが出るはず。

The environment variable GRAPHVIZ_DOT has not been set
Dot executable is c:\Program Files\graphviz-2.38\bin\dot.exe
Dot version: dot - graphviz version 2.38.0 (20140413.2041)
Installation seems OK. File generation OK

Graphvizの場所は探してくれるのね。
指定したい場合は環境変数GRAPHVIZ_DOTに設定すれば良いみたい。

PlantUMLの動作確認

plantuml.jarと同じところにsample.txtを作成。
内容は以下の通り。

@startuml
Class1 -> Class2 : 処理A
Class1 <-- Class2
@enduml

Class1から「処理A」というメッセージを送って、Class2から返ってくるという、簡単なシーケンス図。

plantuml.jarが置いてあるフォルダでコマンドプロンプトを立ち上げて、以下を実行する。

java -jar plantuml.jar sample.txt

すると下記のPNGファイル(sample.png)が生成される。
f:id:nave_kazu:20161125155400p:plain

既定値ではPNGが作成されるので、明示的にPNGを生成したい場合は下記で実行する。

java -jar plantuml.jar -tpng sample.txt

SVGで出力したい場合、-tsvgを指定する。

java -jar plantuml.jar -tsvg sample.txt

するとSVGファイル(sample.svg)が生成される。

PlantUMLの書き方

日本語リファレンスがあるので、これを参考に書いてみよう。

Spring Day 2016に参加してきた

/IT系 /Java

JJUG(日本Javaユーザグループ)のイベントは何度か参加したことがあったが、今回初めてJSUG(日本Springユーザ会)のイベントに参加した。
以下、自分用のメモとして参加したセッションの感想をまとめる。

Spring Framework 5.0 (ROOM1-2)

speakerdeck.com

来年3月にリリース予定のSpring Framework 5.0のお話。
今日の主なテーマは下記の4つ。
・パフォーマンス改善
・JDK9とJigsaw
・HTTP/2とServlet 4.0
・リアクティブアーキテクチャ

その中で一番時間を割いて説明していたのはリアクティブについて。
リアクティブとかノンブロッキングとかの話についていけてないので、このあたりを勉強しないといけない。

災害対策(DR)対応の超ミッション・クリティカル Cloud Foundry は Azure !! (ROOM1-3)

docs.com

Azureは全世界に30のリージョンがあるから、超ミッション・クリティカルなシステムでも運用できますよというお話。
30のリージョンというのはアマゾン ウェブ サービスとGoogle Cloud Platformを合わせた数よりも多いとか。
AWSはいま14リージョン、Google Cloud Platformはいま6リージョン?自分の調べたソースが正しいのか分からないが、確かに合わせた数よりも多い。
日本だけでも東日本と西日本の2箇所があるので、災害対策はバッチリでしょということ。

事例紹介としてFordPass ( https://www.fordpass.com/ ) が紹介されていた。
FordPass、いいな。
駐車場をアプリから予約出来たり、車生活での不安を解消するサービスになると思う。

モノリシックなシステムからマイクロサービスベースのシステムに移行することを薦めていた。
システム開発は建築からいろいろとノウハウを吸収したが、建築は30年の耐用年数で考えて設計するのにシステムはそれよりはるかに短い期間で作り直すのだから、そもそもの思想が違うのだからシステム構築に合った考え方を導入しましょう。その解がマイクロサービスだし、基盤としてCloud Foundry + Azureの組み合わせが良いよという話。

Spring Security で作る Web API アクセス制御の最適解 (ROOM3-5)

www.slideshare.net

個人的にも最近「Web APIのアクセス制御って何が正解なんだろ?」と思うところがあった矢先のこのセッションだった。
「最適解」というタイトルだが結論は「case by case」とのこと。

プライベートネットワーク内であればSSLいらない、外とつながるならSSL必須。
SSL使うならBasic認証もありだけど、SSLも万全ではないしリクエスト毎に静的・無期限のクレデンシャル(ユーザーの認証に用いられる情報)を流すのには抵抗がある。

リクエスト毎に異なるクレデンシャルを流す方法としてHMACを用いたAPIキー署名がある。

OAuth 2.0が例に挙がったが、そもそもOAuthは認証の仕組みでも認可の仕組みでもない。
Aが持っている認可をBに部分委譲するための仕組みがOAuth。
この委譲の仕組みを使ってアクセス制御をする。
詳しくはスライドの神様を参照。

Web APIの認証とは関係ないが登壇者的に面白いと思う技術がOpenID Connect。
詳細はスライドの38枚目を参照。
(OpenID Connectがしてくれること)

Spring5に備えるリアクティブプログラミング入門 (ROOM3-6)

www.slideshare.net

Spring Framework 5.0のお話のときも思ったが、まだまだこのあたりの話についていけてないので勉強しないといけない。

Microservices Architectureを超大規模プロジェクトでやってみた。 (ROOM2-7)

speakerdeck.com

実践的なお話を聞いた。
ビルド時間14時間、.warを動かすのに32Gbyteのメモリが必要なほどの巨大なモノリシックシステムを数年かけて今もマイクロサービス化しているという話。
聞くも涙、語るも涙。

どの単位で切り出すかを手探りでやって、まずは「業務機能での何となくの塊」に分けてマイクロサービス化する。
巨大なシステムだからコストかかる → コストを下げるためにスリム化する → 32Gbyteメモリが1-4Gbyteまでスリム化。

Grailsドメイン駆動設計を実践する時の勘所 (ROOM2-8)

www.slideshare.net

ドメイン駆動設計に惹かれて参加。

GrailsフルスタックWebアプリケーションフレームワークで、「動くものがすぐに作れる」「記述が簡潔」「GORMが強力」「プラグイン機構」という特徴を持っている。
ドメイン駆動設計は「ドメインそのものとドメインのロジックに焦点を置いてシステム設計をする」手法。
自分もエリックさんの本を持っているが、難解で重くて読むのつらい。登壇者は「500ページを超える鈍器」と表現。確かに。

GrailsとDDDの相性は非常に良いらしい。

が、Grailsの知識はなく、DDDの知識も中途半端なので、両方を勉強しなおしてからスライドを再度見させていただこうと思う。

Data Microservices with Spring Cloud Stream, Task, and Data Flow (ROOM2-9)

www.slideshare.net

マイクロサービスをLinux/UNIXのパイプのようにつないで一連の処理を実現しようという話。
パイプ好きにはたまらない仕組み。

Spring Cloud Streamが長命のタスク向き、Spring Cloud Taskが短命のタスク向き。
クラウド上で動くので、スケールアップ・スケールアウトは容易だし、データのルーティングも出来るので細かな制御が可能。

Spring Cloud StreamとSpring Cloud Taskのデモをやってくれた。
PCF Devで試してみよう。

再来週は

次はJJUG CCC 2016 Fallに参加予定。
JJUG CCC 2016 Fall