今からお前んちこいよ

ベルリンにて細々とお勉強。

PHPUnitの逆引きメモ - テストコードの小技

f:id:hakopako03:20181203051352j:plain

自分の知識整理も兼ねて PHPUnitの使い方逆引きメモ を作ってみようと思う。
これまでの職場でなんとなく蓄積した知識を書き出してみる。

結局のところ公式ドキュメントを見ればほぼ全ての詳細が書いてある。 基本的な知識、setUp() / tearDown() / assert などについては既知前提とする。
想定しているバージョンは、

  • PHP 7.1.9
  • PHPUnit 7.1

便利機能

テストデータを書く - @dataProvider

準備したデータをテストメソッドに順番に流し込んでくれる。
公式ページ) データプロバイダ

<?php
use PHPUnit\Framework\TestCase;
use Path\To\Something;

class DataTest extends TestCase
{
    /**
     * @dataProvider somethingProvider    <--- ここにデータプロバイダーメソッド名を書くと読み込んでくれる
     */
    public function testSomething($a, $b, $expected)
    {
        $sth = new Something($a, $b);
        $actual = $sth->bar();
        $this->assertSame($expected, $actual);
    }

    public function somethingProvider()
    {
        return [
            'case aaa 1' => ['aaa', 1, 0],
            'case bbb 1' => ['bbb', 1, 1],
            'case aaa 0' => ['aaa', 0, 1],
            'case bbb 0' => ['bbb', 0, 3]
        ];
    }
}

例外のテストを書く - @expectedException

期待される例外をアノテーションに記述すれば例外をテストしてくれる。
公式ページ)例外のテスト

<?php
use PHPUnit\Framework\TestCase;
use Path\To\Something;

class ExceptionTest extends TestCase
{
    /**
     * @expectedException InvalidArgumentException    <--- ここに期待される例外を記述
     */
    public function testException()
    {
        $sth = new Something($a);
    }
}

特定のテストをスキップする - @require / markTestSkipped()

環境によるスキップなどは @require アノテーション で対処できる。
例えばmysqlに繋げない環境などは以下のようになる。
公式ページ)@requires によるテストのスキップ

<?php
use PHPUnit\Framework\TestCase;

/**
 * @requires extension mysqli    <--- メソッドやクラスに必須条件を記述
 */
class DatabaseTest extends TestCase
{
    public function testConnection()
    {
    }
}
?>

とりあえずスキップしたい場合は以下のようにもできる。

<?php
use PHPUnit\Framework\TestCase;

class DatabaseTest extends TestCase
{
    public function testConnection()
    {
        $this->markTestSkipped();
        // 以降のテストは実行されない
    }
}
?>

依存メソッドをダミーに置き換える - Stub / Mock

例えば以下の doSomething() メソッドをテストしたいとする。
AnotherThing オブジェクトをモックに置き換える。
公式ページ) 9. テストダブル — PHPUnit latest Manual

<?php

class Something {
    public doSomething(string $str, AnotherThing $anotherThing) : string 
    {
        if($anotherThing->isAvailableToDo($str)){
            return $str . " did something";
        }
        return "";
    }
}

class AnotherThing {
    public isAvailableToDo(string $str) : bool 
    {
        // check something here ....
        return true;
    }
}
<?php
use PHPUnit\Framework\TestCase;
use Path\To\Something;
use Path\To\AnotherThing;

class SomethingTest extends TestCase
{
    public function testDoSomething()
    {
        // create mock object.
        $mock = $this->createMock(AnotherThing::class);
        $mock->method('isAvailableToDo')->willReturn(true);

        $something = new Something();
        $actual = $something->doSomething('taro-san', $mock);
        $this->assertEquals('taro-san did something', $actual);
    }
}
?>

小技リスト

private/protected メソッドをテストする - Reflect / Extend

例えば以下のメソッドをテストしたいとする。

<?php

class Something {
    private doSomething(string $str) : string 
    {
        return $str . " did something";
    }
}

方法1: Reflect を使って一時的にテストコードからのアクセスを可能にさせる

<?php 
use PHPUnit\Framework\TestCase;
use Path\To\Something;

class ClosedMethodTest extends TestCase
    public function testDoSomething() 
    {
        $sth = new Something();
        $reflectMethod = new ReflectMethod(Something::class, 'doSomething');
        $reflectMethod->accesible(true);
        $actual = $reflectMethod->invoke($sth, 'taro-san');
        $this->assertEquals('taro-san did something', $actual);
    }
}

方法2: 対象クラスを継承したクラスを使う

<?php
use PHPUnit\Framework\TestCase;
use Path\To\Something;

class SomethingForTest extends Something {
    public doSomething(string $str) : string 
    {
        return parent::doSomething($str);
    }
}

class ClosedMethodTest extends TestCase
    public function testDoSomething() 
    {
        $sth = new SomethingForTest();
        $actual = $sth->doSomething('taro-san');
        $this->assertEquals('taro-san did something', $actual);
    }
}

環境にあったテストをする - phpunit.xml

phpunit.xml には色々な機能がある。
ここではphpに関連した項目をいくつかあげる。

<?xml version="1.0" encoding="UTF-8"?>
<phpunit stopOnFailure="false" bootstrap="autoload.php" >
    <testsuites>
        <testsuite name="Project Test Suite">
            <directory>./tests</directory>
        </testsuite>
    </testsuites>
    <php>
        <ini name="display_errors" value="1" />    ------ ① php.ini を上書きする
        <const name="TEST_ENVIRONMENT" value="ci" />    --- ② global constを追加する
        <env name="FILEPATH" value="/path/to/file"/>    --- ③ 環境変数を設定する
    </php>
</phpunit>

これらを設定することで、dev,test(CI環境),stagingなど環境にあったテストを実行させることができる。 主にCI開発などではよくあると思う。

終わりに

「PHPUnitにこういう機能があったな」「こういう書き方があったな」という引き出しを知っておくだけでもテストコードを書く敷居を下げられると思う。

webエンジニアとして海外転職して1ヶ月経過したので思ったことを書く

webエンジニアとして海外転職して1ヶ月経過したので思ったことを書く。
状況としては、

  • ドイツ、ベルリンに就職&移住
  • 会社は英語
  • 生活はドイツ語(これから勉強する。。。
  • ベルリンオフィス450人中、日本人1人
  • 全社的にも(1000人以上いるはず)日本人1人説がある
  • アジア人がマジでいない
  • まだアジアには進出してない会社だからかもしれない

ヨーロッパの多種多様なアクセントの英語が結構きつい

私は基本的にアメリカ英語を勉強して来てたし、アメリカ人の友達と話すことが今までは多かった。もしくはフィリピン英語。耳が完全に甘やかされていたと思う。ロシア英語、ウクライナ英語、ドイツ英語、ルーマニア英語、フランス英語、ギリシャ英語、エジプト英語、などが日々周りで飛び交ってる英語のアクセントといった感じだ。

マジで聞き取れない

3ヶ国語が普通

一つ目の話と被るかもしれないが、多くの人が3ヶ国語以上喋れる状態に劣等感を感じる。母国語と英語と第二言語という感じである。母国語に加え英語はとりあえず喋れる上で、さらに他の言語で談話してる同僚の姿をよく見る。私は2ヶ国語しか今の段階ではコミュニケーションが取れないので、劣等感というのか、カルチャーショックというのか、多少の井の中の蛙感を感じずにはいられない。

これからドイツ語をマスターしていきたい

英語圏のエンジニアの情報収集力が半端ない

英語で難なく情報収集とアウトプットができるだけで、ここまでスピード感が違うのかという衝撃を日々受けている。OSS活動にもなんの敷居もなく質問を投げたり、コミットしたりなどのやりとりをしている姿が見受けられる。業務での困った状況からネットで情報を集めるスピードと得る情報量が私とは全然違う。そりゃ上達するわ、って顔面パンチを食らったような状態だ。

英語力も今以上に身につけていかないといけないなぁ

総括

つまり、言語力はただのツールなので、そのツールを使いこなせないとエンジニアという本業がただの引き算になってしまうな!って感じだ。今の会社はダイバーシティなところでこんな私にも寛大なのでパフォーマンス出せそうだけれど、クビにならないように頑張ろうと思う。。。

海外転職したエンジニアの面接対策

バックエンドエンジニアとしてドイツに転職した。少し海外転職について調べた方ならお分かりかもしれないが、日本から海外直接雇用されるのは容易ではない。 そんな中、

  • 女性のエンジニアでも海外転職に成功した
  • ”それなり” のエンジニアでも海外転職に成功した*1

という事例から、対策したことを書いてみようと思う。 数え切れないほどの試行錯誤をしてきたが、いくつかに分類されるのでそれぞれをざっくり書いてみる。

基本的な流れとしては、
レジュメ応募→書類審査→スクリーニング面接→コーディング/行動面接が何回か
という順序が多い。

英文職務経歴書を書く

まずはレジュメが通らないと話は始まらない。
過去に 通る英文レジュメへの試行錯誤をまとめる という記事でまとめた。

スクリーニング対策

スクリーニング面接とは

(オレオレ翻訳)
スクリーニング面接とは、企業が求めるポジションに応募者が合致しているかを判断するための面接です。また、一般的には採用プロセスにおける初めの面接とされます。

多くの場合、スクリーニング面接では応募者の経歴を簡単にヒアリングし、応募資格を満たしているかを確認します。質問事項は主に応募資格に関することですが、希望給与額やビザなどの労働資格などを聞くことがあります。

スクリーニング面接は電話越しや対面にて実施されますが、どちらにおいても、次のインタビュープロセスに進むかを決めるものです。

原文にはどうやってスクリーニング面接を突破するかも書いてあるので是非。

ここで気を抜かずにいるとラッキーが降ってくるかもしれない。
より最適な別ポジションでも並行して採用プロセスを進めてくれることがある。
単純にチャンスが2倍になる。

自己紹介を完結にまとめる

個人的に大事だと思うものは自己紹介でのアピールである。 自己紹介のTips記事はいっぱいあるのでググると良い。大体は ”簡潔に” や ”会話の種をまくように単語を混ぜ込んでいく”などの印象だ。 私は30秒程度で言い終わる内容にしていた。

また、自己紹介はスクリーニングに限らずどの面接でも聞かれる。 自分なりの定型文を作り、インタビューの種類によって粒度を変えるようにすると効率が良い。

希望給与額の計算

初めの電話やビデオ通話で希望給与額を聞かれることが多い。後になって希望額を変更するのは大変印象が悪いので、事前にいくらでどの程度の生活水準になるかを調べておく必要がある。例えば、ドイツであれば半分近くが税金として充てられる、などが日本との大きな違いかもしれない。

  • PayScale : ポジション別給与相場
  • glassdoor: ポジション別給与相場
  • Numbeo : 物価/比較
  • Expatistan : 物価/比較
  • 税金は国によって違うのでそれぞれ調べる

インタビュー対策

Coding Test/Interview 対策

海外転職でよく見かける話題はこのコーディングテストに関することだろう。 主にアルゴリズムとデータ構造の問題なのだが、私が使っていたのは以下。

正直なところ私はスターエンジニアでもなく、理系大学院卒でもない*2。難易度の高い問題は到底解けないが、基礎〜ミドルの問題は解けるような学習状態にしていた。また前者の方々は多少英語ができなくても十分海外面接を突破していける部類に入るのだろうが、私の場合は到底そういった勝負はできないので、英語力は困らないレベルまで努力していた。

Behavioral Interview の回答

Behavioral Interview とは

(オレオレ翻訳)
多くの応募者が苦戦する方式が行動面接(Behavioral Interview)です。応募者自身が "なぜそのポジションに最適なのか" を面接官にアピールする機会が行動面接です。 普通の面接では面接官側から応募者が行うであろう業務について説明するでしょう。しかし、この行動面接では応募者がこれまでの経験でどのような状況をくぐり抜け、ハンドリングしてきたのかを細部まで説明します。応募者が ”自己売り” する以上にいい方法がありますか?

原文

それなりなエンジニアの私は実務経験のアピールがとても重要であった。
参考にしたのは、

日本で5,6年ほどの実務経験があるので、その中から面白くなりそうな話を絞り出した。
これも自己紹介文と同様に、自分なりの定型文を作り "簡潔に"、"興味深く" なるよう準備していた。 回答準備した質問は、

  • これまでであなたが成し遂げた一番大きなことはなんですか?
  • これまでで最難関だった事はどんな事でしたか?どの様にそれを乗り越えましたか?
  • あなたの長所はなんですか?
  • あなたの短所はなんですか?
  • あなたを雇うべき理由はなんですか?

など。

会社への質問

これに関しては自分の聞きたいことを聞けばいい。何も質問しないのはネガティブな印象を与えてしまうので何かしら質問事項をストックしておくとスムーズである。
参考にしたのは、

どこでも使えそうな質問は、

  • あなた(面接官)はどうしてこの会社に入ったんですか?
  • どういった技術スタックを使っていますか?
  • どういった人がチームには合わないと思う/かったですか?
  • 仮に私が入社した場合、6ヶ月や1年ほどでどのような成果を期待しますか?

など。

おわりに

うまくいかないことの方が多く、メンタルが削られていくのは避けられないと思う。モチベーションを保つ為に私は以下の記事を何度か読んでいたので是非。

tango-ruby.hatenablog.com

 

*1:スターエンジニアではないが、多分一人前に仕事はできてる

*2:情報系学部卒。職種に関連のない学部だとビザが降りないので注意