
Essayez -le: Phoenix-chat .fly.dev
Un tutoriel étape par étape pour construire, tester et déployer une application de chat à Phoenix!
page_controller_test.exsuser_socket.jsexcoveralls en tant que dépendance (de développement) à mix.exscoveralls.jsonPresence pour suivre qui est en ligne Les applications de chat sont le "Hello World" des exemples en temps réel.
Malheureusement, la plupart des exemples d'applications montrent quelques bases , puis ignorent le reste ...? ♀️
Les débutants sont donc souvent perdus ou confus quant à ce qu'ils devraient faire ou apprendre ensuite !
Très peu de tutoriels envisagent des tests, du déploiement, de la documentation ou d'autres « améliorations » qui font toutes partie du « monde réel » de la création et de l'exécution d'applications; Ce sont donc des sujets que nous couvrirons pour " combler les lacunes ".
Nous avons écrit ce tutoriel comme un moyen le plus simple d'apprendre Phoenix , Ecto et Channels avec un exemple pratique que n'importe qui peut suivre .
C'est l'exemple / tutoriel que nous souhaitons avoir lorsque nous apprenons Elixir , Phoenix ... si vous le trouvez utile, s'il vous plaît merci!
Un simple tutoriel étape par étape vous montrant comment:
mix phx.new chat "générateur" )Fly.io afin que vous puissiez montrer aux gens votre création!Initialement , nous ignorons délibérément les fichiers de configuration et les « internes de Phoenix » parce que vous ( débutants ) n'avez pas besoin de les savoir pour commencer . Mais ne vous inquiétez pas, nous y reviendrons en cas de besoin . Nous favorisons l'apprentissage " juste à temps " ( lorsque vous en avez besoin ), car il est immédiatement évident et pratique pourquoi nous apprenons quelque chose.
Cet exemple est destiné aux débutants complets en tant qu'application " My First Phoenix ".
Nous essayons d' assumer le moins possible, mais si vous pensez que nous avons " sauté une étape " ou si vous vous sentez " coincé " pour quelque raison que ce soit, ou que nous avons des questions ( liées à cet exemple ), veuillez ouvrir un problème sur Github!
Les communautés @dwyl et phoenix sont des super débutants , alors n'ayez pas peur / timide.
De plus, en posant des questions, vous aidez tous ceux qui sont ou pourraient être coincés avec la même chose!
Ces instructions vous montrent comment créer l'application de chat à partir de zéro .
brew install elixirRemarque : Si vous avez déjà installé
Elixirsur votre Mac et que vous souhaitez simplement passer à la dernière version, exécutez:brew upgrade elixir
mix archive.install hex phx_new Les connaissances de base de la syntaxe des élixir aideront,
Veuillez voir: Dwyl / Learn-Elixir
Les connaissances de base JavaScript sont avantageuses ( mais pas essentielles car le code "frontal" est assez basique et bien élaboré ). Voir: Dwyl / Javascript-the-Good-Parts-notes
Vérifiez que vous avez la dernière version d' Elixir ( exécutez la commande suivante dans votre terminal ):
elixir -vVous devriez voir quelque chose comme:
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)Vérifiez que vous avez la dernière version de Phoenix :
mix phx.new -vVous devriez voir:
Phoenix installer v1.7.0-rc.2Remarque : Si votre version
Phoenixest plus récente , n'hésitez pas à mettre à jour ce doc! Nous faisons de notre mieux pour le tenir au courant ... mais vos contributions sont toujours les bienvenues!
Dans ce tutoriel, nous utilisons Phoenix 1.7-RC2, le deuxième candidat à la version pour
Phoenix 1.7. Au moment de la rédaction, si vous installez Phoenix, la dernière version stable n'est pasv1.7. Pour utiliser cette version, suivez le Guide officiel (ne vous inquiétez pas, il s'agit simplement d'exécuter une commande!) -> https://www.phoenixframework.org/blog/phoenix-1.7-ReeededCependant, si vous lisez ceci après sa sortie,
v1.7sera installée pour vous et vous devriez voirPhoenix installer v1.7.0dans votre terminal.
Confirmer que PostgreSQL est en cours d'exécution ( afin que l'application puisse stocker les messages de chat ) exécuter la commande suivante:
lsof -i :5432Vous devriez voir une sortie similaire à ce qui suit:
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) Cela nous indique que PostgreSQL " écoute " sur le port TCP 5432 ( le port par défaut )
Si la commande lsof ne donne aucun résultat dans votre terminal, exécutez:
pg_isreadyIl devrait imprimer ce qui suit:
/tmp:5432 - accepting connectionsAvec tous ces "chèques pré-vols", volons !
Avant d'essayer de construire l'application de chat à partir de zéro, clone et exécuter la version de travail finie pour avoir une idée de ce à quoi s'attendre.
Dans votre terminal, exécutez la commande suivante pour cloner le repo:
git clone [email protected]:dwyl/phoenix-chat-example.git Se transformer en répertoire phoenix-chat-example et installer les dépendances Elixir et Node.js avec cette commande:
cd phoenix-chat-example
mix setupExécutez l'application Phoenix avec la commande:
mix phx.serverSi vous ouvrez l'application localhost: 4000 en deux autres navigateurs Web, vous pouvez voir les messages de chat affichés dans chacun d'eux dès que vous appuyez sur la touche Entrée :

Maintenant que vous avez confirmé que l'application de chat Phoenix finie fonctionne sur votre machine, il est temps de le construire à partir de zéro!
Répertoire de modification:
cd ..Et commencez à construire!
Dans votre programme de terminal sur votre Host local, saisissez la commande suivante pour créer l'application:
mix phx.new chat --no-mailer --no-dashboard --no-gettext Qui créera la structure du répertoire et les fichiers de projet.
Nous exécutons la commande
mix phx.newavec les arguments--no-mailer--no-dashboard--no-gettextparce que nous ne voulons pas que notre projet génére des fichiers Mailer, inclut unPhoenix.LiveDashboardet générer des fichiersgettext(pouri18n).
Lorsqu'on lui a demandé de « récupérer et d'installer des dépendances ? [Yn]»,
Tapez Y dans votre terminal, suivi de la touche Entrée ( retour ).
Vous devriez voir: 
Changer le répertoire dans le répertoire chat en exécutant la commande suggérée:
cd chatExécutez maintenant la commande suivante:
mix setupRemarque : À ce stade, il y a déjà une "application", elle ne fait rien (encore) ...
vous pouvez exécutermix phx.serverdans votre terminal - ne vous inquiétez pas si vous voyez une erreur
Messages, c'est parce que nous n'avons pas encore créé notre base de données.
Nous nous en occuperons à l'étape 6!
Pour l'instant, ouvrez http: // localhost: 4000 dans votre navigateur
Et vous verrez la page d'accueildefault"Bienvenue à Phoenix":

Arrêtez le serveur Phoenix dans votre terminal avec la commande CTRL + C.
Dans votre fenêtre Terminal, exécutez la commande suivante:
mix test
Vous devriez voir une sortie similaire à ce qui suit:
Generated chat app
.....
Finished in 0.02 seconds (0.02s async, 0.00s sync)
5 tests, 0 failures
Randomized with seed 84184Maintenant que nous avons confirmé que tout fonctionne (tous les tests passent), continuons à la partie intéressante !
Générez le canal (WebSocket) à utiliser dans l'application de chat:
mix phx.gen.channel RoomSi vous êtes invité à confirmer l'installation d'un nouveau gestionnaire de socket, Type
yet appuyez sur la touche[Enter].
Cela créera trois fichiers :
* creating lib/chat_web/channels/room_channel.ex
* creating test/chat_web/channels/room_channel_test.exs
* creating test/support/channel_case.exEn plus de créer deux autres fichiers :
* creating lib/chat_web/channels/user_socket.ex
* creating assets/js/user_socket.js Le fichier room_channel.ex gâte à des messages de réception / envoi et le room_channel_test.exs teste l'interaction de base avec le canal. Nous nous concentrerons sur les fichiers socket créés par la suite. ( Ne vous inquiétez pas encore, nous examinerons le fichier de test à l'étape 14 ci-dessous !)
Nous sommes informés que nous devons mettre à jour un morceau de code dans notre application:
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 " Le générateur nous demande d'importer le code client dans le frontend. Faisons cela plus tard. Pour l'instant, ouvrez le fichier lib/chat_web/endpoint.ex et suivez les instructions.
Après cela, ouvrez le fichier appelé /lib/chat_web/channels/user_socket.ex
Et changez la ligne:
channel "room:*" , ChatWeb.RoomChannelà:
channel "room:lobby" , ChatWeb.RoomChannelVérifiez le changement ici.
Cela garantira que tous les messages envoyés à "room:lobby" sont acheminés vers notre RoomChannel .
La "room.* précédente signifiait que tout sous-thèse dans "room" était acheminé. Mais pour l'instant, restons-nous à un seul sous-topique ?.
Pour plus de détails sur les chaînes Phoenix, ( nous vous recommandons fortement ) Lisez: https://hexdocs.pm/phoenix/channels.html
Ouvrez le /lib/chat_web/controllers/page_html/home.html.heex
et copier-coller ( ou type ) le code suivant:
<!-- 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 > Il s'agit du formulaire de base que nous utiliserons pour saisir les messages de chat.
Les classes par exemple w-full et items-center sont des classes TailwindCSS pour styliser le formulaire.
Phoenix inclut Tailwind par défaut afin que vous puissiez vous mettre en place avec votre application / idée / "MVP"!
Si vous êtes nouveau dans
Tailwind, veuillez voir: Dwyl / Learn-TailwindSi vous avez des questions sur l'une des classes
Tailwindutilisées, veuillez passer 2 minutes à googler ou à rechercher les documents officiels (superbes!): Tailwindcss.com/docs et puis si vous êtes toujours coincé, veuillez ouvrir un problème.
Votre fichier /lib/chat_web/controllers/page_html/home.html.heex modèle home.html.heex
Ouvrez le fichier lib/chat_web/components/layouts/root.html.heex et localisez la balise <body> . Remplacez le contenu du <body> par le code suivant:
< 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 > Votre fichier de modèle root.html.heex devrait ressembler à ceci: /lib/chat_web/components/layouts/root.html.heex
À la fin de cette étape, si vous exécutez le serveur Phoenix mix phx.server et affichez l'application dans votre navigateur, il ressemblera à ceci:

Il commence donc déjà à ressembler à une application de chat de base. Malheureusement, puisque nous avons changé la copie de la home.html.heex , notre page_controller_test.exs échoue maintenant:
Exécutez la commande:
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"
Heureusement, c'est facile à réparer.
page_controller_test.exs Ouvrez le fichier test/chat_web/controllers/page_controller_test.exs et remplacez la ligne:
assert html_response ( conn , 200 ) =~ "Peace of mind from prototype to production"Avec:
assert html_response ( conn , 200 ) =~ "Phoenix Chat Example"Maintenant, si vous effectuez à nouveau les tests, ils passeront:
mix test
Exemple de sortie:
........
Finished in 0.1 seconds (0.09s async, 0.06s sync)
8 tests, 0 failures
Randomized with seed 275786
Ouvrez assets/js/app.js , décomancement et modifier la ligne:
import socket from "./user_socket.js" Avec la ligne non éminée , notre application importera le fichier socket.js qui nous donnera des fonctionnalités WebSocket.
Ajoutez ensuite le code JavaScript ("Client") suivant en bas du fichier:
/* 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 ) ;
}Prenez un moment pour lire le code JavaScript et confirmez votre compréhension de ce qu'elle fait.
Espérons que les commentaires en ligne sont explicites, mais si quelque chose n'est pas clair, veuillez demander!
À ce stade, votre fichier app.js devrait ressembler à ceci: /assets/js/app.js
user_socket.js Par défaut, le canal Phoenix (client) s'abonnera à la salle générique: "topic:subtopic" . Étant donné que nous n'allons pas utiliser cela, nous pouvons éviter de voir des erreurs "unable to join: unmatched topic" dans notre navigateur / console en commentant simplement quelques lignes dans le fichier user_socket.js . Ouvrez le fichier dans votre éditeur et localisez les lignes suivantes:
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 ) } )Commentez les lignes afin qu'elles ne soient pas exécutées:
//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) }) Votre user_socket.js devrait maintenant ressembler à ceci: /assets/js/user_socket.js
Si vous décidez ensuite de ranger votre application de chat, vous pouvez
deleteces lignes commentées du fichier.
Nous les gardons simplement pour référence à la façon de rejoindre les canaux et de recevoir des messages.
Si vous exécutez l'application, essayez de remplir les champs name et message et cliquez sur Enter (ou appuyez sur Send ).
Le message doit apparaître sur différentes fenêtres!

Avec cela, nous pouvons continuer.
Si nous ne voulions pas enregistrer l'historique du chat, nous pourrions simplement déployer cette application immédiatement et nous aurions terminé!
En fait, il pourrait s'agir d'une " fonctionnalité " / " fonctionnalité " pour avoir un chat " éphémère " sans aucun historique ... Voir: http://www.psstchat.com/.
Mais nous supposons que la plupart des applications de chat sauvent l'histoire afin que
newpersonnes rejoignant la "chaîne" puissent voir l'histoire et les personnes qui sont brièvement "absentes" peuvent "rattraper" l'histoire.
Exécutez la commande suivante dans votre terminal:
mix phx.gen.schema Message messages name:string message:stringVous devriez voir la sortie suivante:
* creating lib/chat/message.ex
* creating priv/repo/migrations/20230203114114_create_messages.exs
Remember to update your repository by running migrations:
$ mix ecto.migrateDécomposons cette commande pour plus de clarté:
mix phx.gen.schema - La commande mix pour créer un nouveau schéma (table de base de données)Message - Le nom singulier pour enregistrer dans nos messages "Collection"messages - le nom de la collection ( ou table de base de données )name:string - Le nom de la personne envoyant un message, stocké sous forme string .message:string - Le message envoyé par la personne, également stocké sous forme de string . La ligne creating lib/chat/message.ex crée le "schéma" pour notre table de base de données de messages.
De plus, un fichier de migration est créé, par exemple: creating priv/repo/migrations/20230203114114_create_messages.exs La " migration " crée en fait le tableau de la base de données dans notre base de données.
Dans votre terminal, exécutez la commande suivante pour créer le tableau messages :
mix ecto.migratePour le contexte, nous recommandons la lecture: hexdocs.pm/ecto_sql/ ecto.migration .html
Vous devriez voir ce qui suit dans votre terminal:
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.0s Si vous ouvrez votre GUI PostgreSQL ( par exemple: PGADMIN ), vous verrez que le tableau des messages a été créé dans la base de données chat_dev :

Vous pouvez afficher le schéma de table en " clic droit " ( ctrl + click sur Mac ) sur la table messages et sélectionnant "Propriétés":

Remarque : Pour les sections 7, 8 et 9, nous allons étouffer la façon dont notre code "gère" les différents événements qui peuvent se produire dans notre application de chat.
Phoenix résume une grande partie de la logique de passage des messages sous-jacente dans la communication de processus d'Elixir (pour plus d'informations sur la façon dont les processus d'élixir communiquent, lisez ici).
Dans Phoenix, les événements / messages envoyés à partir du client sont automatiquement acheminés vers les fonctions de gestionnaire correspondantes en fonction du nom de l'événement, ce qui rend la gestion des messages sans couture et simple!.
Ouvrez le fichier lib/chat_web/channels/room_channel.ex et à l'intérieur de la fonction def handle_in("shout", payload, socket) do ajoutez la ligne suivante:
Chat.Message . changeset ( % Chat.Message { } , payload ) |> Chat.Repo . insert Pour que votre fonction finisse par ressembler à ceci:
def handle_in ( "shout" , payload , socket ) do
Chat.Message . changeset ( % Chat.Message { } , payload ) |> Chat.Repo . insert
broadcast socket , "shout" , payload
{ :noreply , socket }
end Si vous avez remarqué plus tôt, dans notre fichier assets/js/app.js , nous avons utilisé la fonction sendMessage() pour pousser notre message vers le serveur sur l'événement "Shout".
Phoenix achemine le message vers la fonction handle_in("shout", payload, socket) parce que le nom de l'événement correspond au «shout».
Dans cette fonction, nous gérons la charge utile (qui est le texte du message et toute autre donnée) et l'insertons dans notre base de données. Soigné!
Ouvrez le fichier lib/chat/message.ex et importez Ecto.Query :
defmodule Chat.Message do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query # add Ecto.Query
Puis ajoutez-y une nouvelle fonction:
def get_messages ( limit \ 20 ) do
Chat.Message
|> limit ( ^ limit )
|> order_by ( desc: :inserted_at )
|> Chat.Repo . all ( )
end Cette fonction accepte une limit de paramètre unique pour ne renvoyer qu'un nombre fixe / maximum d'enregistrements. Il utilise all fonction ECTO pour récupérer tous les enregistrements de la base de données. Message est le nom du schéma / table pour lequel nous voulons obtenir des enregistrements, et la limite est le nombre maximal d'enregistrements à récupérer.
Dans le fichier /lib/chat_web/channels/room_channel.ex créez une nouvelle fonction:
@ 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 et en haut du fichier mettez à jour la fonction join à ce qui suit:
def join ( "room:lobby" , payload , socket ) do
if authorized? ( payload ) do
send ( self ( ) , :after_join )
{ :ok , socket }
else
{ :error , % { reason: "unauthorized" } }
end
endRemarque : Comme la section 7, Phoenix sait appeler cette fonction lorsque le serveur envoie le message interne
:after_joinvia le processus de canal.Notre fonction
join/3danslib/chat_web/channels/room_channel.exenvoie ce:after_join messageau processus de chaîne lorsque le client se connecte avec succès au sujet"room:lobby".
Démarrez le serveur Phoenix ( s'il n'est pas déjà en cours d'exécution ):
mix phx.serverRemarque : il faudra quelques secondes pour compiler .
Dans votre terminal, vous devriez voir:
[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… Cela nous indique que notre code a compilé ( comme prévu ) et que l'application de chat s'exécute sur TCP Port 4000 !
Ouvrez l'application Web de chat dans deux Windows de navigateur séparé : http: // localhost: 4000
( Si votre machine n'a qu'un seul navigateur, essayez d'utiliser un onglet "incognito" )
Vous devriez pouvoir envoyer des messages entre les deux fenêtres du navigateur: 
Félicitations! Vous avez une application de chat ( de base ) fonctionnelle écrite dans Phoenix!
L'historique du chat (message) est enregistré !
Cela signifie que vous pouvez rafraîchir le navigateur ou rejoindre un navigateur différent et que vous verrez toujours l'histoire!
Les tests automatisés sont l'un des meilleurs moyens d'assurer la fiabilité de vos applications Web.
Remarque : Si vous êtes entièrement nouveau dans les tests automatisés ou le «développement piloté par les tests» («TDD»), nous vous recommandons de lire / suivre le didacticiel «Basic»: github.com/dwyl/ Learn-TDD
Les tests dans Phoenix sont rapides ( les tests s'exécutent en parallèle! ) Et facile à démarrer! Le framework de test ExUnit est intégré, il n'y a donc pas de "décisions / débats" sur le cadre ou le style à utiliser.
Si vous n'avez jamais vu ou écrit de test avec ExUnit , ne craignez pas, la syntaxe doit être familière si vous avez écrit un type de test automatisé dans le passé.
Chaque fois que vous créez une nouvelle application Phoenix ou ajoutez une nouvelle fonctionnalité ( comme un canal ), Phoenix génère un nouveau test pour vous.
Nous exécutons les tests à l'aide de la commande mix test :
... ... ..
Finished in 0.1 seconds ( 0.05 s async , 0.06 s sync )
8 tests , 0 failures
Randomized with seed 157426Dans ce cas, aucun de ces tests échoue. ( 8 tests, 0 défaillance )
Cela vaut la peine de prendre un moment ( ou tant que vous avez besoin !) Pour comprendre ce qui se passe dans le fichier /room_channel_test.exs . Ouvrez -le si vous ne l'avez pas déjà fait, lisez les descriptions et le code des tests.
Pour un peu de contexte, nous vous recommandons de lire: https://hexdocs.pm/phoenix/ test_channels .html
Jetons un coup d'œil au premier test dans /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 Le test obtient le socket de la fonction setup ( sur la ligne 6 du fichier ) et attribue le résultat de l'appel de la fonction push vers une variable ref push simplement un message ( la carte %{"hello" => "there"} ) sur le socket au sujet "ping" .
La clause Fonction handle_in qui gère le sujet "ping" :
def handle_in ( "ping" , payload , socket ) do
{ :reply , { :ok , payload } , socket }
end Répond simplement avec la charge utile que vous l'envoyez, donc dans notre test, nous pouvons utiliser la macro assert_reply pour affirmer que la ref est égale à :ok, %{"hello" => "there"}
Remarque : Si vous avez des questions ou si vous avez besoin d' aide à comprendre les autres tests, veuillez ouvrir un problème sur GitHub, nous sommes heureux de le développer davantage!
( Nous essayons juste de garder ce tutoriel raisonnablement "bref" afin que les débutants ne soient pas "dépassés" par quoi que ce soit ...)
Souvent, nous pouvons en apprendre beaucoup sur une application ( ou API ) en lisant les tests et en voyant où se trouvent les «lacunes» dans les tests.
Heureusement, nous pouvons y parvenir avec seulement quelques étapes:
excoveralls en tant que dépendance (de développement) à mix.exs Ouvrez votre fichier mix.exs et trouvez la fonction "DEPS":
defp deps do
Ajoutez une virgule à la fin de la dernière ligne, puis ajoutez la ligne suivante à la fin de la liste:
{ :excoveralls , "~> 0.15.2" , only: [ :test , :dev ] } # tracking test coverage De plus, trouvez la section def project do ( vers le haut de mix.exs ) et ajoutez les lignes suivantes à la liste:
test_coverage : [ tool : E xCoveralls ] ,
preferred_cli_env: [
coveralls : :test ,
"coveralls.detail": :test ,
"coveralls.post": :test ,
"coveralls.html": :test
] Ensuite , installez la dépendance sur excoveralls que nous venons d'ajouter à mix.exs :
mix deps.getVous devriez voir:
Resolving Hex dependencies...
Dependency resolution completed:
* Getting excoveralls (Hex package)
... etc.coveralls.json Dans le "Root" ( répertoire de base ) du projet de chat, créez un nouveau fichier appelé coveralls.json et copier-coller ce qui suit:
{
"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 "
]
}
Ce fichier est assez basique, il demande à l'application coveralls de nécessiter une minimum_coverage de 100% ( c'est-à-dire que tout est testé 1 ) et d' ignorer les fichiers du test/ répertoire pour la vérification de la couverture. Nous ignorons également des fichiers tels que application.ex , telemetry.ex , core_components.ex et user_socket.ex car ils ne sont pas pertinents pour les fonctionnalités de notre projet.
1 Nous pensons qu'investir un peu de temps à l'avance pour rédiger des tests pour tout notre code en vaut la peine pour avoir moins de bugs plus tard.
Les bogues sont chers , les tests sont bon marché et la confiance / la fiabilité est inestimable .
Pour exécuter les tests avec une couverture, copiez la commande de coche suivante dans votre terminal:
MIX_ENV = test mix do coveralls . json
Pour une utilisation Windows:
$ env :MIX_ENV = "test" ; mix do coveralls . json
Vous devriez voir:
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%
----------------
Comme nous pouvons être ici, seulement 80% des lignes de code dans /lib sont "couvertes" par les tests que nous avons écrits.
Pour afficher la couverture dans un navigateur Web, exécutez ce qui suit:
MIX_ENV = test mix coveralls . html ; open cover / excoveralls . html Cela ouvrira le rapport de couverture (HTML) dans votre navigateur Web par défaut:

Ouvrez le fichier test/chat_web/channels/room_channel_test.exs et ajoutez le test suivant:
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 Enfin, à l'intérieur de lib/chat_web/router.ex , commentez le morceau de code suivant.
pipeline :api do
plug :accepts , [ "json" ]
end Puisque nous n'utilisons pas ceci :api dans ce projet, il n'est pas nécessaire de le tester.
Maintenant, lorsque vous exécutez MIX_ENV=test mix do coveralls.json vous devriez voir:
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%
----------------
Ce test crée simplement un message avant l' subscribe_and_join , il y a donc un message dans la base de données à envoyer à n'importe quel clien qui rejoint le chat.
De cette façon, le :after_join a au moins un message et l' Enum.each sera invoqué au moins une fois.
Avec cela, notre application est entièrement testée!
Nous pouvons étendre ce projet pour prendre en charge l'authentification de base. Si vous souhaitez comprendre comment l'authentification est implémentée de la manière facile / rapide , voir: Auth.md
Presence pour suivre qui est en ligne L'un des grands avantages de l'utilisation Phoenix est que vous pouvez facilement suivre les processus et les canaux.
Cela ouvre la voie à montrer sans effort qui est en ligne ou non!
Si vous souhaitez développer cette fonctionnalité, nous avons créé un guide en presence.md juste pour vous! ?
L'intégration continue vous permet d'automatiser l'exécution des tests pour vérifier / confirmer que votre application fonctionne comme prévu ( avant le déploiement ). Cela empêche de " briser " accidentellement votre application.
Heureusement, les étapes sont assez simples.
Pour un exemple ci.yml , voir:
.github/workflows/ci.yml
Le déploiement à Fly.io prend quelques minutes, nous vous recommandons de suivre le guide officiel: fly.io/docs/elixir/ get-started
Une fois que vous aurez déployé , vous pourrez afficher / utiliser votre application dans n'importe quel navigateur Web / mobile.
par exemple: Phoenix-chat .fly.dev /

Si vous avez trouvé cet exemple utile, veuillez ️ le référentiel GitHub afin que nous ( et autres ) sachez que vous l'avez aimé!
Si vous voulez en savoir plus Phoenix et la magie de LiveView , pensez à lire le tutoriel de notre débutant: github.com/dwyl/ phoenix-liveview -counter-tutorial
Pour une version d'une application de chat utilisant LiveView , vous pouvez lire le référentiel suivant: github.com/dwyl/ phoenix-liveview-chat-exemple
Merci d'avoir appris avec nous! ☀️
Ce repo est inspiré par l'exemple de chat simple de @ chrismccord: https://github.com/chrismccord/phoenix_chat_example ❤️
Au moment de la rédaction de l'exemple de Chris a été mis à jour pour la dernière fois le 20 février 2018 et utilise Phoenix 1.3 Voir: Problèmes / 40.
Il existe plusieurs différences (changements de rupture) entre Phoenix 1.3 et 1.6 ( la dernière version ).
Notre tutoriel utilise Phoenix 1.6.2 (plus tard en octobre 2021). Notre espoir est qu'en écrivant ( et en maintenant ) un tutoriel axé sur étape par étape, nous contribuons à la communauté Elixir / Phoenix sans s'accumuler PRS sur le repo de Chris.