
試してみてください: phoenix-chat .fly.dev
Phoenixでチャットアプリを構築、テスト、展開するための段階的なチュートリアル!
page_controller_test.exsを更新しますuser_socket.jsの行をコメントしますexcoverallsを(開発) mix.exs関係として追加しますcoveralls.jsonという新しいファイルを作成しますPresenceを追加して、誰がオンラインであるかを追跡しますチャットアプリは、リアルタイムの例の"Hello World"です。
悲しいことに、ほとんどの例アプリはいくつかの基本を示してから残りを無視します...?
したがって、初心者は、次に何をすべきか、または学ぶべきことに関して、紛失または混乱したままになることがよくあります!
すべては、アプリの構築と実行の「現実世界」の一部であるテスト、展開、ドキュメント、またはその他の「機能強化」を考慮しています。したがって、これらは「ギャップを埋める」ためにカバーするトピックです。
このチュートリアルは、フェニックス、 Ecto 、 Channels Phoenix最も簡単な方法で、誰もが従うことができる実用的な例で学習するために書きました。
これは、 Elixir 、 Phoenix学んでいたときに望んでいた例/チュートリアルです...あなたがそれが役立つと思うなら、ありがとう!
方法を示す簡単なステップバイステップのチュートリアル:
mix phx.new chat "Generator"コマンドを使用)Fly.ioに展開して、人々にあなたの創造物を見せることができます!最初は、設定ファイルと「フェニックスの内部」を意図的にスキップします。なぜなら、あなた(初心者)は、開始するためにそれらについて知る必要がないからです。しかし、心配しないでください、私たちは必要に応じて彼らに戻ります。私たちは、「ジャストインタイム」(必要なとき)学習を好みます。なぜなら、私たちが何かを学んでいる理由はすぐに明白で実用的だからです。
この例は、「私の最初のフェニックス」アプリとしての完全な初心者向けです。
私たちはできるだけ少ないと仮定しようとしますが、私たちが「ステップをスキップした」と思うか、何らかの理由で「立ち往生している」と思うか、質問がある(この例に関連する)場合は、githubで問題を開いてください!
@DWylとフェニックスのコミュニティはどちらも非常に初心者に優しいので、恐れてはいけません。
また、質問をすることで、あなたは同じことに固執するかもしれない、あるいは立ち往生するかもしれないすべての人を助けています!
これらの指示は、チャットアプリをゼロから作成する方法を示しています。
brew install elixir注:既に
ElixirがMacにインストールされていて、最新バージョンにアップグレードするだけの場合は、実行:brew upgrade elixir
mix archive.install hex phx_new基本的なElixir構文の知識が役立ちます、
参照してください:Dwyl/ Learn-Elixir
基本的なJavaScriptの知識は有利です(ただし、「フロントエンド」コードは非常に基本的で十分に対応しているため、不可欠ではありません)。参照:dwyl/javascript-the-good-parts-notes
Elixirの最新バージョンがあることを確認してください(端末で次のコマンドを実行してください):
elixir -vあなたは次のようなものを見る必要があります:
Erlang/OTP 25 [erts-13.1.1] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Elixir 1.14.1 (compiled with Erlang/OTP 25)Phoenixの最新バージョンがあることを確認してください。
mix phx.new -vあなたは見るべきです:
Phoenix installer v1.7.0-rc.2注:
Phoenixバージョンが新しい場合は、このドキュメントを自由に更新してください!私たちはそれを最新の状態に保つために最善を尽くします...しかし、あなたの貢献はいつでも大歓迎です!
このチュートリアルでは、
Phoenix 1.7の2番目のリリース候補であるPhoenix 1.7-RC2を使用しています。執筆時点では、Phoenixをインストールする場合、最新の安定したバージョンはv1.7ではありません。このバージョンを使用するには、公式ガイドに従ってください(心配しないでください、1つのコマンドを実行しているだけです!) - > https://www.phoenixframework.org/blog/phoenix-1.7リリースただし、リリース後にこれを読んでいる場合は、
v1.7がインストールされ、ターミナルにPhoenix installer v1.7.0表示されます。
postgresqlが実行されていることを確認します(アプリはチャットメッセージを保存できます)次のコマンドを実行します。
lsof -i :5432次のような出力が表示されるはずです。
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
postgres 529 Nelson 5u IPv6 0xbc5d729e529f062b 0t0 TCP localhost:postgresql (LISTEN)
postgres 529 Nelson 6u IPv4 0xbc5d729e55a89a13 0t0 TCP localhost:postgresql (LISTEN)これは、PostgreSQLがTCPポート5432 (デフォルトポート)で「リスニング」されていることを示しています
lsofコマンドが端末で結果をもたらさない場合は、次のことを実行します。
pg_isready以下を印刷する必要があります。
/tmp:5432 - accepting connectionsこれらすべての「飛行前のチェック」が実行された状態で、飛びましょう!
チャットアプリをゼロから構築しようとする前に、クローンを作成し、完成したバージョンを実行して、何を期待するかを理解してください。
端末で次のコマンドを実行して、リポジトリをクローンします。
git clone [email protected]:dwyl/phoenix-chat-example.gitphoenix-chat-exampleディレクトリに変更し、このコマンドを使用してElixirとNode.js両方の依存関係をインストールします。
cd phoenix-chat-example
mix setupコマンドでフェニックスアプリを実行します。
mix phx.serverApp Localhost:4000を開くと、さらに2つのWebブラウザーで、 Enterキーを押すとすぐにチャットメッセージが表示されます。

完成したフェニックスチャットアプリがマシンで機能することを確認したので、今度はゼロから構築する時が来ました!
ディレクトリの変更:
cd ..そして、構築を始めましょう!
LocalHostのターミナルプログラムで、次のコマンドを入力してアプリを作成します。
mix phx.new chat --no-mailer --no-dashboard --no-gettextこれにより、ディレクトリ構造とプロジェクトファイルが作成されます。
--no-mailer--no-dashboard--no-gettext引数を使用してmix phx.newコマンドを実行しています。プロジェクトにメーラーファイルを生成したくないため、Phoenix.LiveDashboardを含めてgettextファイルを生成するため(i18nの場合)。
「依存関係を取得してインストールするように頼まれたとき?[yn]」、
ターミナルにyを入力し、その後にEnter ( return )キーが続きます。
あなたは見るべきです: 
提案されたコマンドを実行することにより、ディレクトリをchatディレクトリに変更します。
cd chat次のコマンドを実行します。
mix setup注:この時点で、すでに(まだ)何もしない「アプリ」が既にあります...
ターミナルでmix phx.serverを実行できます- エラーが表示されている場合は心配しないでください
メッセージ、これはまだデータベースを作成していないためです。
ステップ6で世話をします!
今のところ、ブラウザでhttp:// localhost:4000を開きます
そして、default「フェニックスへようこそ」ホームページが表示されます。

Ctrl + Cコマンドを使用して、端末のPhoenixサーバーをシャットダウンします。
ターミナルウィンドウで、次のコマンドを実行します。
mix test
次のような出力が表示されるはずです。
Generated chat app
.....
Finished in 0.02 seconds (0.02s async, 0.00s sync)
5 tests, 0 failures
Randomized with seed 84184すべてが機能していることを確認したので(すべてのテストが通過します)、興味深い部分に進みましょう!
チャットアプリで使用する(websocket)チャネルを生成します。
mix phx.gen.channel Room新しいソケットハンドラータイプ
yのインストールを確認し、[Enter]キーを押すように求められている場合。
これにより、 3つのファイルが作成されます。
* creating lib/chat_web/channels/room_channel.ex
* creating test/chat_web/channels/room_channel_test.exs
* creating test/support/channel_case.exさらに2つのファイルを作成することに加えて、
* creating lib/chat_web/channels/user_socket.ex
* creating assets/js/user_socket.js room_channel.exファイルは、メッセージの受信/送信メッセージとroom_channel_test.exsファイルを処理し、チャネルとの基本的な対話をテストします。その後作成されたsocketファイルに焦点を当てます。 (これについてはまだ心配しないでください。以下のステップ14のテストファイルを見ていきます!)
アプリでコードを更新する必要があることが通知されています。
Add the socket handler to your ` lib/chat_web/endpoint.ex ` , for example:
socket " /socket " , ChatWeb.UserSocket,
websocket: true,
longpoll: false
For the front-end integration, you need to import the ` user_socket.js `
in your ` assets/js/app.js ` file:
import " ./user_socket.js "ジェネレーターは、フロントエンドでクライアントコードをインポートするように依頼します。後でそれをしましょう。今のところ、 lib/chat_web/endpoint.exファイルを開き、手順に従ってください。
この後、 /lib/chat_web/channels/user_socket.ex chat_web/channels/user_socket.exというファイルを開きます
そして、行を変更します:
channel "room:*" , ChatWeb.RoomChannelに:
channel "room:lobby" , ChatWeb.RoomChannelここで変更を確認してください。
これにより"room:lobby"に送信されるメッセージがRoomChannelにルーティングされることが保証されます。
以前の"room.* "room"内のサブトピックがルーティングされたことを意味していました。しかし、今のところ、1つのサブトピックに絞り込みましょう。
Phoenixチャンネルの詳細については(強くお勧めします)https://hexdocs.pm/phoenix/channels.html
/lib/chat_web/controllers/page_html/home.html.heexファイルを開きます
次のコードをコピー(またはタイプ)します。
<!-- The list of messages will appear here: -->
< div class =" mt-[4rem] " >
< ul id =" msg-list " phx-update =" append " class =" pa-1 " > </ ul >
</ div >
< footer class =" bg-slate-800 p-2 h-[3rem] fixed bottom-0 w-full flex justify-center " >
< div class =" w-full flex flex-row items-center text-gray-700 focus:outline-none font-normal " >
< input type =" text " id =" name " placeholder =" Name " required
class =" grow-0 w-1/6 px-1.5 py-1.5 " />
< input type =" text " id =" msg " placeholder =" Your message " required
class =" grow w-2/3 mx-1 px-2 py-1.5 " />
< button id =" send " class =" text-white bold rounded px-3 py-1.5 w-fit
transition-colors duration-150 bg-sky-500 hover:bg-sky-600 " >
Send
</ button >
</ div >
</ footer >これは、チャットメッセージを入力するために使用する基本的なフォームです。
w-fullやitems-centerなどのクラスは、フォームをスタイリングするためのTailwindCSSクラスです。
PhoenixにはデフォルトでTailwindが含まれているため、アプリ/Idea/"MVP"でアップアンドランニングを行うことができます!
Tailwindが初めての場合は、Dwyl/ Learn-Tailwindをご覧ください使用されている
Tailwindクラスのいずれかについて質問がある場合は、2分間グーグルでグーグルでグーグルしたり検索したりしてください(Superb!)docs:tailwindcss.com/docsを使用してください。
home.html.heexテンプレートファイルは次のようになります: /lib/chat_web/controllers/page_html/home.html.heex
lib/chat_web/components/layouts/root.html.heexファイルを開き、 <body>タグを見つけます。 <body>の内容を次のコードに置き換えます。
< body class =" bg-white antialiased min-h-screen flex flex-col " >
< header class =" bg-slate-800 w-full h-[4rem] top-0 fixed flex flex-col justify-center z-10 " >
< div class =" flex flex-row justify-center items-center " >
< h1 class =" w-4/5 md:text-3xl text-center font-mono text-white " >
Phoenix Chat Example
</ h1 >
</ div >
</ header >
< %= @inner_content % >
</ body > root.html.heexテンプレートファイルは次のようになります: /lib/chat_web/components/layouts/root.html.heex
このステップの最後に、Phoenix Server mix phx.serverを実行し、ブラウザでアプリを表示すると、次のようになります。

そのため、すでに基本的なチャットアプリのように見え始めています。悲しいことに、 home.html.heexのコピーを変更してからpage_controller_test.exsが失敗しました:
コマンドを実行します:
mix test 1) test GET / (ChatWeb.PageControllerTest)
test/chat_web/controllers/page_controller_test.exs:4
Assertion with =~ failed
code: assert html_response(conn, 200) =~ "Peace of mind from prototype to production"
ありがたいことに、これは簡単に修正できます。
page_controller_test.exsを更新しますtest/chat_web/controllers/page_controller_test.exsファイルを開き、行を置き換えます。
assert html_response ( conn , 200 ) =~ "Peace of mind from prototype to production"と:
assert html_response ( conn , 200 ) =~ "Phoenix Chat Example"今、あなたが再びテストを実行すると、彼らは合格します:
mix test
サンプル出力:
........
Finished in 0.1 seconds (0.09s async, 0.06s sync)
8 tests, 0 failures
Randomized with seed 275786
Open assets/js/app.js 、conement and Change of Line:
import socket from "./user_socket.js"行がないと、アプリはsocket.jsファイルをインポートして、WebSocket機能を提供します。
次に、次のJavaScript( "Client")コードをファイルの下部に追加します。
/* Message list code */
const ul = document . getElementById ( 'msg-list' ) ; // list of messages.
const name = document . getElementById ( 'name' ) ; // name of message sender
const msg = document . getElementById ( 'msg' ) ; // message input field
const send = document . getElementById ( 'send' ) ; // send button
const channel = socket . channel ( 'room:lobby' , { } ) ; // connect to chat "room"
channel . join ( ) ; // join the channel.
// Listening to 'shout' events
channel . on ( 'shout' , function ( payload ) {
render_message ( payload )
} ) ;
// Send the message to the server on "shout" channel
function sendMessage ( ) {
channel . push ( 'shout' , {
name : name . value || "guest" , // get value of "name" of person sending the message. Set guest as default
message : msg . value , // get message text (value) from msg input field.
inserted_at : new Date ( ) // date + time of when the message was sent
} ) ;
msg . value = '' ; // reset the message input field for next message.
window . scrollTo ( 0 , document . documentElement . scrollHeight ) // scroll to the end of the page on send
}
// Render the message with Tailwind styles
function render_message ( payload ) {
const li = document . createElement ( "li" ) ; // create new list item DOM element
// Message HTML with Tailwind CSS Classes for layout/style:
li . innerHTML = `
<div class="flex flex-row w-[95%] mx-2 border-b-[1px] border-slate-300 py-2">
<div class="text-left w-1/5 font-semibold text-slate-800 break-words">
${ payload . name }
<div class="text-xs mr-1">
<span class="font-thin"> ${ formatDate ( payload . inserted_at ) } </span>
<span> ${ formatTime ( payload . inserted_at ) } </span>
</div>
</div>
<div class="flex w-3/5 mx-1 grow">
${ payload . message }
</div>
</div>
`
// Append to list
ul . appendChild ( li ) ;
}
// Listen for the [Enter] keypress event to send a message:
msg . addEventListener ( 'keypress' , function ( event ) {
if ( event . key === `Enter` && msg . value . length > 0 ) { // don't sent empty msg.
sendMessage ( )
}
} ) ;
// On "Send" button press
send . addEventListener ( 'click' , function ( event ) {
if ( msg . value . length > 0 ) { // don't sent empty msg.
sendMessage ( )
}
} ) ;
// Date formatting
function formatDate ( datetime ) {
const m = new Date ( datetime ) ;
return m . getUTCFullYear ( ) + "/"
+ ( "0" + ( m . getUTCMonth ( ) + 1 ) ) . slice ( - 2 ) + "/"
+ ( "0" + m . getUTCDate ( ) ) . slice ( - 2 ) ;
}
// Time formatting
function formatTime ( datetime ) {
const m = new Date ( datetime ) ;
return ( "0" + m . getUTCHours ( ) ) . slice ( - 2 ) + ":"
+ ( "0" + m . getUTCMinutes ( ) ) . slice ( - 2 ) + ":"
+ ( "0" + m . getUTCSeconds ( ) ) . slice ( - 2 ) ;
}JavaScriptコードを読んで、それが何をしているかについての理解を確認してください。
うまくいけば、インラインコメントが自明であることを願っていますが、何かが不明確な場合は、尋ねてください!
この時点で、 app.jsファイルは次のようになります: /assets/js/app.js
user_socket.jsの行をコメントしますデフォルトでは、Phoenixチャンネル(クライアント)がジェネリックルーム"topic:subtopic"を購読します。これを使用することはないので、 user_socket.jsファイルの数行にコメントするだけで、ブラウザー/コンソールの"unable to join: unmatched topic"エラーが表示されることを避けることができます。エディターでファイルを開き、次の行を見つけます。
let channel = socket . channel ( "room:42" , { } )
channel . join ( )
. receive ( "ok" , resp => { console . log ( "Joined successfully" , resp ) } )
. receive ( "error" , resp => { console . log ( "Unable to join" , resp ) } )それらが実行されないように行にコメントしてください:
//let channel = socket.channel("room:42", {})
//channel.join()
// .receive("ok", resp => { console.log("Joined successfully", resp) })
// .receive("error", resp => { console.log("Unable to join", resp) }) user_socket.jsは次のようになります: /assets/js/user_socket.js
後でチャットアプリを整理することにした場合は、ファイルからコメントした行
deleteできます。
チャネルに参加してメッセージを受信する方法を参照するために、それらを維持しています。
アプリを実行している場合は、 nameとmessageフィールドを入力して、 Enter (またはSend ]を押す)をクリックしてください。
メッセージは別のウィンドウに表示されます!

これが完了すると、続行できます。
チャット履歴を保存したくない場合は、すぐにこのアプリを展開することができ、完了します!
実際、履歴なしに「はかない」チャットをするのは「ユースケース」/「機能」である可能性があります... http://www.psstchat.com/を参照してください。
しかし、ほとんどのチャットアプリは履歴を保存して、「チャンネル」に参加する
new人が歴史を見ることができ、「不在」の人が歴史に「追いつく」ことができると仮定しています。
端末で次のコマンドを実行します。
mix phx.gen.schema Message messages name:string message:string次の出力が表示されます。
* creating lib/chat/message.ex
* creating priv/repo/migrations/20230203114114_create_messages.exs
Remember to update your repository by running migrations:
$ mix ecto.migrate明確にするためにそのコマンドを分解しましょう:
mix phx.gen.schema - 新しいスキーマを作成するミックスコマンド(データベーステーブル)Message - メッセージ「コレクション」のレコードの特異名messages - コレクションの名前(またはデータベーステーブル)name:string - stringとして保存されたメッセージを送信する人の名前。message:string - 人から送信されたメッセージもstringとして保存されます。 creating lib/chat/message.ex行は、メッセージデータベーステーブルの「スキーマ」を作成します。
さらに、移行ファイルが作成されます。たとえば: creating priv/repo/migrations/20230203114114_create_messages.exs 「移行」は、実際にデータベースにデータベーステーブルを作成します。
端末で次のコマンドを実行して、 messagesテーブルを作成します。
mix ecto.migrateコンテキストについては、hexdocs.pm/ ecto_sql / ecto.migration .htmlを読むことをお勧めします
端末に次のように表示されます。
11:42:10.130 [info] == Running 20230203114114 Chat.Repo.Migrations.CreateMessages.change/0 forward
11:42:10.137 [info] create table messages
11:42:10.144 [info] == Migrated 20230203114114 in 0.0sPostgreSQL GUI(例:PGADMIN )を開くと、メッセージテーブルがchat_devデータベースで作成されていることがわかります。

messagesテーブルで「右クリック」( ctrl + click )して「プロパティ」を選択することで、テーブルスキーマを表示できます。

注:セクション7、8、および9では、チャットアプリで発生する可能性のあるさまざまなイベントをコードがどのように「処理」するかを具体化します。
Phoenixは、Elixirのプロセス通信における基礎となるメッセージパスロジックの多くを抽象化します(Elixirプロセスの通信方法の詳細については、こちらをご覧ください)。
Phoenixでは、クライアントから送信されたイベント/メッセージは、イベント名に基づいて対応するハンドラー関数に自動的にルーティングされ、メッセージ処理がシームレスで簡単です!
lib/chat_web/channels/room_channel.exファイルを開き、関数内のdef handle_in("shout", payload, socket) do次の行を追加します。
Chat.Message . changeset ( % Chat.Message { } , payload ) |> Chat.Repo . insert あなたの機能がこのように見えてしまうように:
def handle_in ( "shout" , payload , socket ) do
Chat.Message . changeset ( % Chat.Message { } , payload ) |> Chat.Repo . insert
broadcast socket , "shout" , payload
{ :noreply , socket }
end先ほど気づいた場合は、 assets/js/app.jsファイルで、function sendMessage()を使用して「shout」イベントでメッセージをサーバーにプッシュしました。
Phoenixは、イベント名が「Shout」と一致するため、メッセージをサーバー側のhandle_in("shout", payload, socket)関数にルーティングします。
この関数では、ペイロード(メッセージテキストおよびその他のデータ)を処理し、データベースに挿入します。きちんとした!
lib/chat/message.exファイルを開き、 Ecto.Queryをインポートします:
defmodule Chat.Message do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query # add Ecto.Query
次に、新しい関数を追加します。
def get_messages ( limit \ 20 ) do
Chat.Message
|> limit ( ^ limit )
|> order_by ( desc: :inserted_at )
|> Chat.Repo . all ( )
endこの関数は、単一のパラメーターlimitを受け入れ、固定/最大数のレコード数のみを返します。 Ectoのall関数を使用して、データベースからすべてのレコードを取得します。 Message 、記録を取得したいスキーマ/テーブルの名前であり、制限はフェッチするレコードの最大数です。
/lib/chat_web/channels/room_channel.exファイルで新しい関数を作成します。
@ impl true
def handle_info ( :after_join , socket ) do
Chat.Message . get_messages ( )
|> Enum . reverse ( ) # revers to display the latest message at the bottom of the page
|> Enum . each ( fn msg -> push ( socket , "shout" , % {
name: msg . name ,
message: msg . message ,
inserted_at: msg . inserted_at ,
} ) end )
{ :noreply , socket } # :noreply
endそして、ファイルの上部で、 join関数を次のものに更新します。
def join ( "room:lobby" , payload , socket ) do
if authorized? ( payload ) do
send ( self ( ) , :after_join )
{ :ok , socket }
else
{ :error , % { reason: "unauthorized" } }
end
end注:セクション7と同様に、フェニックスは、サーバーが内部メッセージを
:after_joinしたときにこの関数を呼び出すことを知っています。
lib/chat_web/channels/room_channel.exでのjoin/3関数は:after_join messageします"room:lobby"
Phoenixサーバーを開始します(まだ実行されていない場合):
mix phx.server注:コンパイルには数秒かかります。
ターミナルに表示されるはずです。
[info] Running ChatWeb.Endpoint with cowboy 2.8.0 at 0.0.0.0:4000 (http)
[info] Access ChatWeb.Endpoint at http://localhost:4000
webpack is watching the files…これにより、コードがコンパイルされ(予想どおり)、チャットアプリがTCPポート4000で実行されていることがわかります。
2つの別々のブラウザウィンドウでチャットWebアプリを開きます:http:// localhost:4000
(マシンに1つのブラウザのみがある場合は、1つの「Incognito」タブを使用してみてください)
2つのブラウザウィンドウ間にメッセージを送信できるはずです。 
おめでとう! Phoenixで作成された(基本的な)チャットアプリがあります!
チャット(メッセージ)履歴が保存されます!
これは、ブラウザを更新したり、別のブラウザに参加したりできることを意味しますが、それでも履歴が表示されます。
自動テストは、Webアプリケーションで信頼性を確保するための最良の方法の1つです。
注:自動テストまたは「テスト駆動型開発」(「TDD」)を完全に新しい場合は、「基本的な」チュートリアル: github.com/dwyl/ Learn-tddを読むことをお勧めします。
フェニックスでのテストは高速で(テストは並行して実行されます! )、簡単に開始できます! ExUnit Testingフレームワークは組み込まれているため、どのフレームワークまたは使用するかについての「決定/議論」はありません。
ExUnitでテストを見たり書いたりしたことがない場合は、心配しないでください。過去に自動化されたテストを作成した場合、構文は馴染みがあるはずです。
新しいPhoenixアプリを作成したり、新しい機能(チャンネルなど)を追加したりするたびに、Phoenixは新しいテストを生成します。
mix testコマンドを使用してテストを実行します。
... ... ..
Finished in 0.1 seconds ( 0.05 s async , 0.06 s sync )
8 tests , 0 failures
Randomized with seed 157426この場合、これらのテストはどれも失敗しません。 ( 8回のテスト、 0の障害)
/room_channel_test.exsファイルで何が起こっているのかを理解するには、少し時間をとる価値があります(または必要な限り!)。まだ開いていない場合は、テストの説明とコードをお読みください。
少しのコンテキストについては、読むことをお勧めします:https://hexdocs.pm/phoenix/ testing_channels .html
/test/chat_web/channels/room_channel_test.exsの最初のテストを見てみましょう。
test "ping replies with status ok" , % { socket: socket } do
ref = push socket , "ping" , % { "hello" => "there" }
assert_reply ref , :ok , % { "hello" => "there" }
endテストでは%{"hello" => "there"} setup関数(ファイルの6行目)からsocketを取得し、 push関数を変数refに呼び出す"ping"をpush socket 。
"ping"トピックを処理するhandle_in関数節:
def handle_in ( "ping" , payload , socket ) do
{ :reply , { :ok , payload } , socket }
end送信するペイロードで単純に返信するため、テストではassert_replyマクロを使用して、 refが次のことに等しいと主張できます:ok, %{"hello" => "there"}
注:質問がある場合、または他のテストを理解するのに役立つ場合は、GitHubで問題を開いてください。これをさらに拡大させていただきます。
(私たちはこのチュートリアルを合理的に「簡潔」に保とうとしているので、初心者は何でも「圧倒されない」ことはありません...)
多くの場合、テストを読んで、テストの「ギャップ」がどこにあるかを確認することから、アプリケーション(またはAPI )について多くのことを学ぶことができます。
ありがたいことに、私たちはいくつかのステップしか得られないことでこれを達成することができます:
excoverallsを(開発) mix.exs関係として追加しますmix.exsファイルを開き、「deps」関数を見つけます:
defp deps do
最後の行の最後にコンマを追加し、リストの最後に次の行を追加します。
{ :excoveralls , "~> 0.15.2" , only: [ :test , :dev ] } # tracking test coverageさらに、 def project doを見つけて( mix.exsの上部に向かって)セクションを見つけ、次の行をリストに追加します。
test_coverage : [ tool : E xCoveralls ] ,
preferred_cli_env: [
coveralls : :test ,
"coveralls.detail": :test ,
"coveralls.post": :test ,
"coveralls.html": :test
]次に、 mix.exsに追加したばかりのexcoverallsに依存します。
mix deps.getあなたは見るべきです:
Resolving Hex dependencies...
Dependency resolution completed:
* Getting excoveralls (Hex package)
... etc.coveralls.jsonという新しいファイルを作成しますチャットプロジェクトの「root」(ベースディレクトリ)で、 coveralls.jsonと呼ばれる新しいファイルを作成し、次のことをコピーします。
{
"coverage_options" : {
"minimum_coverage" : 100
},
"skip_files" : [
" test/ " ,
" lib/chat/application.ex " ,
" lib/chat_web.ex " ,
" lib/chat_web/telemetry.ex " ,
" lib/chat_web/components/core_components.ex " ,
" lib/chat_web/channels/user_socket.ex "
]
}
このファイルは非常に基本的なものであり、 coverallsアプリに100%のminimum_coverage (つまり、すべてがテストされている)を要求し、カバレッジチェックのためにtest/ディレクトリのファイルを無視するよう指示します。また、プロジェクトの機能に関連していないため、 application.ex 、 telemetry.ex 、 core_components.ex 、 user_socket.exなどのファイルも無視します。
1私たちは、すべてのコードのテストを書くために少し前に投資することは、後で少ないバグを持つことは価値があると考えています。
バグは高価で、テストは安価で、自信/信頼性は貴重です。
カバレッジでテストを実行するには、次のコマンドを端末にコピーします。
MIX_ENV = test mix do coveralls . json
Windowsの使用:
$ env :MIX_ENV = "test" ; mix do coveralls . json
あなたは見るべきです:
Randomized with seed 527109
----------------
COV FILE LINES RELEVANT MISSED
100.0% lib/chat.ex 9 0 0
100.0% lib/chat/message.ex 26 4 0
100.0% lib/chat/repo.ex 5 0 0
70.0% lib/chat_web/channels/room_channel.ex 46 10 3
100.0% lib/chat_web/components/layouts.ex 5 0 0
100.0% lib/chat_web/controllers/error_html.ex 19 1 0
100.0% lib/chat_web/controllers/error_json.ex 15 1 0
100.0% lib/chat_web/controllers/page_controller 9 1 0
100.0% lib/chat_web/controllers/page_html.ex 5 0 0
100.0% lib/chat_web/endpoint.ex 49 0 0
66.7% lib/chat_web/router.ex 27 3 1
[TOTAL] 80.0%
----------------
ここでできるように、 /libのコードラインの80%のみが、私たちが書いたテストによって「カバー」されています。
Webブラウザでカバレッジを表示するには、以下を実行します。
MIX_ENV = test mix coveralls . html ; open cover / excoveralls . htmlこれにより、デフォルトのWebブラウザーにカバレッジレポート(HTML)が開きます。

test/chat_web/channels/room_channel_test.exsファイルを開き、次のテストを追加します。
test ":after_join sends all existing messages" , % { socket: socket } do
# insert a new message to send in the :after_join
payload = % { name: "Alex" , message: "test" }
Chat.Message . changeset ( % Chat.Message { } , payload ) |> Chat.Repo . insert ( )
{ :ok , _ , socket2 } = ChatWeb.UserSocket
|> socket ( "person_id" , % { some: :assign } )
|> subscribe_and_join ( ChatWeb.RoomChannel , "room:lobby" )
assert socket2 . join_ref != socket . join_ref
end最後に、 lib/chat_web/router.exの内部で、次のコードにコメントします。
pipeline :api do
plug :accepts , [ "json" ]
endこれを使用していないため:api使用しているため、テストする必要はありません。
MIX_ENV=test mix do coveralls.json 。
Randomized with seed 15920
----------------
COV FILE LINES RELEVANT MISSED
100.0% lib/chat.ex 9 0 0
100.0% lib/chat/message.ex 26 4 0
100.0% lib/chat/repo.ex 5 0 0
100.0% lib/chat_web/channels/room_channel.ex 46 10 0
100.0% lib/chat_web/components/layouts.ex 5 0 0
100.0% lib/chat_web/controllers/error_html.ex 19 1 0
100.0% lib/chat_web/controllers/error_json.ex 15 1 0
100.0% lib/chat_web/controllers/page_controller 9 1 0
100.0% lib/chat_web/controllers/page_html.ex 5 0 0
100.0% lib/chat_web/endpoint.ex 49 0 0
100.0% lib/chat_web/router.ex 27 2 0
[TOTAL] 100.0%
----------------
このテストは、 subscribe_and_joinの前にメッセージを作成するだけなので、データベースにメッセージがあり、チャットに参加するClienに送信するメッセージがあります。
そうすれば、 :after_joinは少なくとも1つのメッセージがあり、 Enum.each少なくとも1回は呼び出されます。
それで、私たちのアプリは完全にテストされています!
このプロジェクトを拡張して、基本認証をサポートできます。認証がどのように実装されているかを簡単/高速な方法で理解したい場合は、auth.mdを参照してください
Presenceを追加して、誰がオンラインであるかを追跡しますPhoenixを使用することの大きな利点の1つは、プロセスとチャネルを簡単に追跡できることです。
これは、誰がオンラインであるかどうかを簡単に示す方法を開きます!
この機能の開発に興味がある場合は、あなたのためだけにpresence.mdガイドを作成しました。 ?
継続的な統合を使用すると、テストの実行を自動化して、アプリが予想どおりに機能していることを確認/確認できます(展開する前に)。これにより、誤ってアプリを「壊す」ことができなくなります。
ありがたいことに、手順は非常に簡単です。
ci.yml例については、以下を参照してください。
.github/workflows/ci.yml
fly.ioへの展開には数分かかります。公式ガイド(fly.io/docs/elixir/getsing-started )をフォローすることをお勧めします
展開すると、任意のWeb/モバイルブラウザでアプリを表示/使用できます。
例: phoenix-chat .fly.dev/

この例が便利だと思ったら、githubリポジトリをお願いします。
フェニックスとLiveViewの魔法をもっと知りたい場合は、初心者のチュートリアルを読むことを検討してください:github.com/dwyl/ phoenix-liveview-counter-tutorial
LiveViewを使用したチャットアプリケーションのバージョンについては、次のリポジトリをお読みください:github.com/dwyl/ phoenix-liveview-chat-example
私たちと一緒に学んでくれてありがとう! ☀☀️
このレポは、 @chrismccordのシンプルなチャットの例に触発されています:https://github.com/chrismccord/phoenix_chat_example
執筆時点で、Chrisの例は2018年2月20日に最後に更新され、Phoenix 1.3を使用しています:Issues/40を参照してください。
Phoenix 1.3と1.6(最新バージョン)の間には、かなりの数の違いがあります(壊れた変更)。
私たちのチュートリアルでは、Phoenix 1.6.2 (2021年10月の最新)を使用しています。私たちの希望は、段階的な初心者に焦点を当てたチュートリアルを書く(そして維持する)ことで、クリスのリポジトリにPRを積み上げることなく、Elixir/Phoenixコミュニティに貢献することです。