たけるのプログラミング

作ったものとか、気ままにアップします。

【PHP PHPUnit】PHPUnitを使って簡単な単体テストを行う

参考にした記事
PHPUnitでユニットテスト① 導入編 | Points & Lines
1. アサーション — PHPUnit latest Manual



PHPUnitを触ってみた。

PHPUnit単体テスト(ユニットテスト)を行うためのフレームワーク

実際に導入から簡単なテストを行ってみた。

composerが導入されていることを前提としている。

1.PHPUnitのインストール

まずプロジェクトのディレクトリで以下のようなコマンドを実行する。

composer require --dev phpunit/phpunit ^latest

以下のコードを使って、バージョン情報を表示されたらインストール成功。

vendor/bin/phpunit --version  

2.テスト対象のクラスを書く


<?php

namespace app;

class Sample 
{
    public function plus100($num) 
    {
        $num += 100;
        return $num;
    }
}

オートロードを指定するためにnamespaceを記述している。そのためcomposer.jsonでは以下のように記述しておく。

{
    "require-dev": {
        "phpunit/phpunit": "^9.5"
    },
    "autoload": {
        "psr-4": {
            "app\\": "./"
        }
    }
}

3.テストコードを書く

<?php
require_once("vendor/autoload.php");

use PHPUnit\Framework\TestCase;
use app\Sample;

class SampleTest extends TestCase
{
    public function testplus100_1()
    {
        $sample = new Sample();
        
        $result = $sample->plus100(-1);
        
        $this->assertGreaterThan(100,$result);
    }

    public function testplus100_2()
    {
        $sample = new Sample();
        
        $result = $sample->plus100(1);
        
        $this->assertGreaterThan(100,$result);
    }

}

今回使ったアサーションメソッドはassertGreaterThan()

4.テスト実行&実行結果

vendor/bin/phpunit SampleTest.php
F.                                                                  2 / 2 (100%)

Time: 00:00.036, Memory: 4.00 MB

There was 1 failure:

1) SampleTest::testplus100_1
Failed asserting that 99 is greater than 100.

Fはアサーションに失敗した際に表示される。.はテストが成功した際に表示される。
3. コマンドラインのテストランナー — PHPUnit latest Manual

There was 1 failureと書いてあるように、1つ失敗があり、
testplus100_1でアサーションに失敗したよう。


今後はもう少し複雑なテストを書いてみたい。

【JavaScript】プロトタイプについて【継承 プロトタイプチェーン】

この記事はJavaScriptの経験が浅い人が書いています。
間違っている点等ございましたら、ご指摘いただけると幸いです。

参考にした記事
【JavaScript】 プロトタイプとは?prototypeプロパティはプロトタイプではない件について
JavaScriptのプロトタイプからオブジェクト指向を学ぶ - Qiita
Object のプロトタイプ - ウェブ開発を学ぶ | MDN


プロトタイプはメモリの節約継承機能を提供するものです。

例えば以下のようなコード

const str = new String('hello');

Stringオブジェクトをインスタンス化します。

strはStringオブジェクトなので、感覚的にStringオブジェクトで用意されているメソッドを使えることは分かります。

例えば以下のようなコード

console.log(str.charAt(2));

ここで開発者ツールを使ってstrの構造を見てみたいと思います。

するとcharAtメソッドが定義されていないことが分かります。ではなぜcharAtメソッドを使えることができるのか。





結論から書くと、prototypeの仕組みによって上記のようにメソッドを使うことができています。

例えば以下のコード

const a = new b;

aの[[Prototype]]にb.prototypeの参照を渡している

つまり上記に書いた

const str = new String('hello');

strの[[Prototype]]にString.prototypeの参照を渡しているということになります。

なので先ほど使ったメソッドのcharAtはString.prototypeオブジェクトのプロパティとして登録されているはずです。

証拠としてドキュメントには以下のように定義されています。

String.prototype.charAt

String.prototype.charAt() - JavaScript | MDN

【JavaScript】Promiseのthenメソッドの挙動について ちょっとした確認

この記事はJavaScript経験浅く、Promise勉強したての人が書いています。
間違い等ございましたら、指摘やアドバイス等いただけると嬉しいです。


thenメソッドはPromiseオブジェクトを返す。

なのでチェーンすることが可能です。

例えばサーバーからfetchメソッドを使ってjson形式のデータを取得してみる

fetch('https://jsonplaceholder.typicode.com/users')
.then(function(response){
    return response.json();
})
.then(function(json){
    console.log(json);
});

まずこのコードをconsole.logで出力すると以下のようになる。

fetch('https://jsonplaceholder.typicode.com/users')

Promiseオブジェクトを返している。そのため後にthenメソッドを続けることができる。
またPromiseResultの値がResponseオブジェクトになっている。

次にこのコードの戻り値をconsole.logで出力すると以下のようになる。

.then(function(response){
    return response.json();
})

thenメソッドの引数にコールバック関数を使用しており、そのコールバック関数の引数であるresponseに①のPromiseResultの値が格納されている。

上記でも書いたようにthenメソッドはPromiseを返す。注目するべき点はコールバック関数内でreturnした値(response.json)がPromiseResultの値となっていること。
つまり①→②のようにPromiseResultの値を 後に続くthenメソッドのコールバック関数の引数の値として利用することができる。

補足

例えばthenのコールバック関数の戻り値にPromiseを指定したらどうなるのか試してみた。

console.log(
    fetch('https://jsonplaceholder.typicode.com/users')
    .then(function(response){
    //return response.json();
    return Promise.resolve(100);
})
);

内定者ポータルサイトのメール通知機能が機能してなかったから、LINEに通知できるシステムを作った。

タイトル通り内定先のポータルサイトのメール通知機能が機能してなかった。

なので人事の方からのメッセージが来ているかいちいちログインして確認しなくては行けなかった。

結構めんどくさい。なのでサイトが更新(新着メッセージが来たら)されたらLINEに通知される仕組みを作った。

結構簡単で、

  • curlを使ってhtmlソースを取得する。
  • 取得したhtmlソースから正規表現を使って、個々のメッセージを囲んでいるdiv要素を全て抜き出し配列に格納。配列の要素数がメッセージ総数となる。
  • (実行1回目)メッセージ総数を適当なテキストファイルに記録。DB使ってもいいけど、大袈裟かも。
  • (実行2回目以降)メッセージ総数とテキストファイルに記録されている数を比べて異なる場合(この場合、メッセージが削除された時も通知しちゃうけど)、メッセージ総数をテキストファイルに上書きして、webhookを使ってLINEに通知する。

またこのシステムを自動で動かすためにcronを使う。

0 */1 * * * php /Applications/MAMP/htdocs/実行したいファイル.php

みたいにすることで毎時 0分に1時間おきにプログラムを実行してくれる。
crontab の書き方|プログラムメモ

LINEに通知が来るたび、しっかり機能してる〜!と思い嬉しい気持ちになってる。

では!

【laravel】modelクラスのドキュメントにwhereメソッドないけど何で?

よくこんなコードを参考書やネットで見かける。

 $items = Tweet::where('name','=','レブロンジェームス')->get();

ここでTweetモデルはIlluminate/Database/Eloquent/Modelを継承してるから、Modelのドキュメントにwhereメソッドについて載ってると考えた。

しかし
Illuminate\Database\Eloquent\Model | Laravel API

をみてみるとwhereメソッドがない。

どういうこと?

以下の記事が解決してくれた。

【Laravel5】Eloquent ORMと2つのBuilderクラス|Laravel|PHP|開発ブログ|株式会社Nextat(ネクスタット)

Illuminate\Database\Eloquent\Model | Laravel API
Illuminate\Database\Eloquent\Builder | Laravel API
Illuminate\Database\Query\Builder | Laravel API

【SQL】内部結合と外部結合について簡単な説明

※たぶん徐々に追記していきます。


今回のサンプルテーブル

membersテーブル

id name team_id
1 レブロンジェームス 1
2 クリスポール 2
3 デビンブッカー 2
4 カールアンソニータウンズ 3
5 デマーデローザン 4

teamsテーブル

id name
1 レイカーズ
2 サンズ
3 ウルブス

内部結合

結合条件のカラムのフィールドの値が一致するレコードのみ取得する。
今回の例だと、membersテーブルのteam_idの値とteamsのidの値が一致したものを結合し、レコードを取得している。逆に一致していないとレコードを取得しない。デローザンのteam_idカラムのフィールド値の4はteamsテーブルのidカラムのフィールド値に存在しないため結合もできないし、レコードも取得されない。


SELECT * FROM members m JOIN teams t ON t.id = m.team_id

id name team_id id name
1 レブロンジェームス 1 1 レイカーズ
2 クリスポール 2 2 サンズ
3 デビンブッカー 2 2 サンズ
4 カールアンソニータウンズ 3 3 ウルブス






外部結合

基準のテーブルを決めて、そのテーブルの全レコードを取得し、内部結合同様に結合条件を利用して結合する。

LEFT JOIN

leftなのでmembers m LEFT JOIN teams tにおけるmembersテーブルを基準とする。

SELECT * FROM members m LEFT JOIN teams t ON t.id = m.team_id

id name team_id id name
1 レブロンジェームス 1 1 レイカーズ
2 クリスポール 2 2 サンズ
3 デビンブッカー 2 2 サンズ
4 カールアンソニータウンズ 3 3 ウルブス
5 デマーデローザン 4 NULL NULL

基準であるmembersテーブルの全レコードを取得したが、m.team_id = 4 = t.id となるレコードがなかったため t.id と t.nameがnullとなっている。
内部結合の場合はそもそもm.id m.nameも取得されない。

RIGHT JOIN

left joinの考え方と同じ

SELECT * FROM members m RIGHT JOIN teams t ON t.id = m.team_id

id name team_id id name
1 レブロンジェームス 1 1 レイカーズ
2 クリスポール 2 2 サンズ
3 デビンブッカー 2 2 サンズ
4 カールアンソニータウンズ 3 3 ウルブス

【PHP】正規表現を学ぶ【その1】

Linuxを勉強してたら正規表現の知識が乏しかったので勉強する。

正規表現をざっとまとめた。
引用参照url
正規表現 | 任意の一文字にマッチする:ドット(.)
正規表現 | いずれか一文字にマッチする:角括弧([...])
正規表現 | バックスラッシュ(\)+文字を使った文字クラスの略記法
正規表現 | 文字列の先頭と文字列の末尾の位置にマッチする:キャレット(^) ドル記号($)
正規表現 | 単語の先頭と単語の末尾にマッチする:\b \B
正規表現 | 直前の文字の繰り返し:* + ? {num} {min,max}


基本的なやつ

* 任意の一文字にマッチする
[] 中のいずれかにマッチする。[0-9]0から9 [a-z]aからz [A-Z]AからZ また[^]とすることで中で指定した文字以外とマッチする
\d 数字
\D 数字以外
\w 英数字とアンダーバー
\W 英数字とアンダーバー以外
\s 空白文字
\S 空白文字以外
^ 文字列の先頭にマッチする
$ 文字列の末尾にマッチする
\b 単語の先頭または単語の末尾にマッチする
\B 単語の先頭及び単語の末尾以外にマッチする
量指定
{num} 直前の文字をnum回繰り返す
{min,} 直前の文字をmin回以上繰り返す
{,max} 直前の文字をmax回以下繰り返す
{min,max} 直前の文字をmin以上、max以下繰り返す
* 直前の文字が0回以上連続する
+ 直前の文字が一回以上連続する
? 直前の文字が0か1回現れる

他にも
()によるグループ化や| (縦線)を使ったパターンといったテクニックもある。

このqiitaの表が上に載ってないやつも載っててわかりやすい。
正規表現まとめ - Qiita

実際にPHP正規表現を使ってみる。

今回PHPの関数であるpreg_matchを使う。引数として渡す文字列に対して正規表現による検索を行う。
PHP: preg_match - Manual

実際に使ってみる。
ユーザーが携帯電話番号を入力したとして、それが本当に携帯電話番号かpreg_matchを使って検証する。

<?php
//携帯電話番号
$test_data = '080-0000-0000';
echo preg_match('/^(070|080|090)-\d{4}-\d{4}$/',$test_data,$array);
var_dump($array);

実行結果

1
array(2) {
  [0]=>
  string(13) "080-0000-0000"
  [1]=>
  string(3) "080"
}

文字列の中に正規表現のパターンがあったので返り値として1が返されています。もしなかった場合は0が帰ってきます。また第三引数に配列を渡してあげることで、その配列に検索結果を代入することができます。
[0]にパターン全体にマッチしたテキストが代入されます。また[1]にはサブパターンにマッチした文字列が代入されるとありました。このサブパターンとは
PHP: サブパターン - Manual
によると

丸カッコで括られたパターンのこと

とあった。つまり上の例の(070|080|090)。このサブパターンに080がマッチしているので、この080が[1]に代入されている。このように2番目のサブパーンにマッチした文字は[2]に代入される。