さて、3月もやってきました。毎月1本Masoniteの記事です。
今回のテーマは、LaravelとMasoniteで全く同じ処理をTDDで作って比較してみよう!です。作るものについては簡単に、ログイン画面だけ作ってみようと思います。
ほとんど同じアーキテクチャを持つフレームワークを、ただ言語がPHPとPythonで違うだけで、どれだけ違いがあるのか、またどれほど同じなのか。じっくり見ていきたいと思います。
Masoniteとはなんぞや?とお思いの方は、まずはこちらの記事を読んでいただければ幸いです。
LaravelそっくりなPython製WebフレームワークMasoniteの紹介
また、TDD(テスト駆動開発)についてご存じない方は、こちらを見ていただければいいと思います。
テスト駆動開発
50 分でわかるテスト駆動開発
ではさっそく、それぞれの言語のインストールから。
インストール
anyenvのインストール
複数の言語を1台のPCにインストールするので、anyenvを入れてからphpenv、pyenvを使ってそれぞれの言語をインストールしてみようと思います。
git clone https://github.com/riywo/anyenv ~/.anyenv
echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
anyenv install --init
exec $SHELL -l
PHP、Pythonのインストール
phpenv、pyenvをインストールします
anyenv install phpenv pyenv
そしてphpenv、pyenvでそれぞれ最新版を検索し、インストールします
mkdir php
cd php
phpenv install -l #インストールできるバージョン一覧を表示
phpenv install 7.3.2
phpenv local 7.3.2
php --version
PHP 7.3.2 (cli) (built: Mar 3 2019 02:51:55) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.2, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.2, Copyright (c) 1999-2018, by Zend Technologies
with Xdebug v2.7.0rc1, Copyright (c) 2002-2019, by Derick Rethans
mkdir python
cd python
pyenv install -l #インストールできるバージョン一覧を表示
pyenv install 3.7.2
pyenv local 3.7.2
python -V
Python 3.7.2
現在のディレクトリ構造はこのようになっています
.
├── php
│ └── .php-version
└── python
└── .python-version
プロジェクト作成
Laravel
まず、phpのパッケージ管理ソフトであるcomposerをインストールします。
apt install -y composer
composer #確認
↓
Download composer.phar ...
All settings correct for using Composer
Downloading...
Composer (version 1.8.4) successfully installed to: /tmp/composer.phar
Use it: php /tmp/composer.phar
Move composer.phar to /home/user/.anyenv/envs/phpenv/versions/7.3.2/composer
______
/ ____/___ ____ ___ ____ ____ ________ _____
/ / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
/_/
Composer version 1.8.4 2019-02-11 10:52:10
phpディレクトリ内でComposerを使用しLaravelインストーラをダウンロードして、laravelコマンドを使ってprojectという名前のプロジェクトを作ります。
composer global require laravel/installer
laravel new project
Masonite
pythonディレクトリ内でvenvを作り、pipを使用しmasonite-cliをダウンロードして、craftコマンドを使ってprojectという名前のプロジェクトを作ります。
python -m venv .venv #venvを作る
source .venv/bin/activate #venvを有効化
pip install masonite-cli #Masoniteをインストール
craft new project #projectという名前でMasoniteプロジェクト作成
cd project/
craft install #Masoniteの依存パッケージ類をインストール
LaravelとMasoniteがインストールできたら、それぞれ組み込みサーバーを立ち上げて、ブラウザからアクセスしてみましょう。Laravelは8001番ポート、Masoniteは8002番ポートを使うことにします。
それぞれphp/projectディレクトリ、python/projectディレクトリでコマンドを実行します。
php artisan serve --port 8001
craft serve -p 8002
さぁ、TDDを始めるぞ
ログイン画面を作る
/loginにアクセスした時に表示される、ログイン画面を作りましょう。
テスト
まずは、ユニットテストを作ります。Laravelではコマンドでテストが作れるのですが、Masoniteではまだテストを作るコマンドは用意されていません。
Laravel
php artisan make:test LoginTest --unit
php/project/tests/Unit/LoginTest.phpが自動生成されました。phpunitコマンドを使ってテストを実行してみましょう。
vendor/bin/phpunit tests/Unit/LoginTest.php
現在は$this->assertTrue(true);
しかテストに書かれていないので、成功すると思います。/loginにアクセスした時にアクセスできる
というテストを書いてみましょう。
class LoginTest extends TestCase
{
/**
* A basic unit test example.
*
* @return void
*/
public function test_loginにアクセスした時にアクセスできる()
{
$response = $this->get('/login')->assertStatus(200);
}
}
ここで再度テストを実行すると、まだルーティングを作っていないので、期待通り失敗します。
Masonite
ではMasoniteでもテストを作っていきます。
from masonite.testing.UnitTest import UnitTest
class TestLogin(UnitTest):
def test_example(self):
assert True
テストを動かしてみましょう。
python -m pytest tests/unit/test_login.py
現在はassert True
しかテストに書かれていないので、成功すると思います。/loginにアクセスした時にアクセスできる
というテストを書いてみましょう。
from masonite.testing.UnitTest import UnitTest
class TestLogin(UnitTest):
def test_loginにアクセスした時にアクセスできる(self):
assert self.route('/login')
ここで再度テストを実行すると、まだルーティングを作っていないので、期待通り失敗します。
では、/loginにアクセスすると、LoginControllerのindexメソッドが呼ばれて、loginビューが表示される、という処理を作っていきます。
ビューを作る
Laravel
まず、ビューを作ります。LaravelのテンプレートエンジンであるBladeには、テンプレートの継承と言って、ビューの中にビューを埋め込むという処理ができます。これによって、jsやcssのインポート処理、ヘッダーやフッターの定義などは一箇所で済ませることができます。
Laravelにはビューを作るコマンドは無いので、手動でファイルを作ります。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title')</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
</head>
<body>
<div id="content">
@yield('content')
</div>
</body>
</html>
@extends('layouts.app')
@section('title', 'ログイン')
@section('content')
<p>Laravelログイン画面</p>
@endsection
Masonite
LaravelのBladeと同じく、Masoniteが採用しているテンプレートエンジンであるJinja2も、継承を使ったテンプレートの埋め込みが使えます。
Laravelの時と同じくlayouts/appとloginを作ります。Masoniteはコマンドでビューが作れます。
craft view layouts/app
craft view login
python/project/resources/templates/layouts/app.html
python/project/resources/templates/login.html
が自動生成されました。ここにHTMLを書いていきます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{% endblock %}</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
</head>
<body>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
{% extends "layouts/app.html" %}
{% block title %}ログイン{% endblock %}
{% block content %}
<p>Masoniteログイン画面</p>
{% endblock %}
コントローラーを作る
Laravel
コマンドでコントローラーを作ります
php artisan make:controller LoginController
indexメソッドを作り、login.blade.phpを返す処理を書きます。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class LoginController extends Controller
{
public function index(){
return view('login');
}
}
Masonite
コマンドでコントローラーを作ります
craft controller Login
indexメソッドを作り、login.htmlを返す処理を書きます。
""" A LoginController Module """
class LoginController:
"""LoginController
"""
def index(self):
return view('login.html')
ルーティングを張る
Laravel
php/project/routes/web.phpに、/loginにgetでアクセスされたらLoginコントローラーのindexメソッドを呼び出す処理を書きます。
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::get('/login', 'LoginController@index'); //←追加
Masonite
python/project/routes/web.pyに、/loginにgetでアクセスされたらLoginコントローラーのindexメソッドを呼び出す処理を書きます。
"""Web Routes."""
from masonite.routes import Get, Post
from masonite.helpers.routes import get #←追加
ROUTES = [
Get().route('/', 'WelcomeController@show').name('welcome'),
get('/login', 'LoginController@index') #←追加
]
テスト
ではここで再度テストを実行してみましょう。
vendor/bin/phpunit tests/Unit/LoginTest.php
python -m pytest tests/unit/test_login.py
ルーティング、コントローラー、ビューの一連の処理を書いたために、テストが成功すると思います。
PHPUnit 7.5.6 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 199 ms, Memory: 16.00 MB
OK (1 test, 1 assertion)
================================ test session starts ================================
platform linux -- Python 3.7.2, pytest-3.10.1, py-1.8.0, pluggy-0.9.0
rootdir: /to/your/dir/python/project, inifile:
collected 1 item
tests/unit/test_login.py . [100%]
======================= 1 passed, 0 warnings in 0.37 seconds ========================
また、組み込みサーバーを起動して/loginにアクセスすると、正しくアクセスできるようになっているでしょう。
php artisan serve --port 8001
craft serve -r -p 8002
まとめ
今回はLaravelとMasoniteについて、インストール、テスト、ビュー、テンプレートエンジン、コントローラー、ルーティングについて比較しました。テストにおける若干の違いは否めませんが、大きな違いはまだ無いように思えます。
後編ではマイグレーション、モデル、ORM、バリデーションについてもTDDをベースに比較してみたいと思います。好ご期待!