🚧 Attention, peinture fraîche !
Cette page a été traduite par une seule personne et n'a pas été relue et vérifiée par quelqu'un d'autre ! Les informations peuvent par exemple être erronées, être formulées maladroitement, ou contenir d'autres types de fautes.
Test du serveur TCP
Passons désormais au test de notre fonction gestion_connexion
.
D'abord, nous avons besoin d'un TcpStream
avec lequel travailler.
Dans un test d'intégration ou de bout-à-bout, nous serions tentés de faire une
vraie connexion TCP pour tester notre code.
Une des stratégies pour faire cela est de démarrer un écouteur sur le port 0 de
localhost
.
Le port 0 n'est pas un port UNIX valide, mais il fonctionne pour faire des
tests.
Le système d'exploitation va obtenir un port TCP ouvert pour nous.
Dans cet exemple, nous allons plutôt écrire un test unitaire pour le
gestionnaire de connexion, pour vérifier que les réponses correctes soient
retournées à leurs entrées respectives.
Pour faire en sorte que notre test unitaire soit isolé et déterminé, nous
allons remplacer le TcpStream
par un mock.
Pour commencer, nous allons changer la signature de gestion_connexion
pour
faciliter ses tests.
En fait, gestion_connexion
ne nécessite pas de async_std::net::TcpStream
,
il a juste besoin d'une structure qui implémente async_std::io::Read
,
async_std::io::Write
, et marker::Unpin
.
Changeons la signature du type dans ce sens nous permet de lui passer un mock
pour la tester.
use std::marker::Unpin;
use async_std::io::{Read, Write};
async fn gestion_connexion(mut stream: impl Read + Write + Unpin) {
Ensuite, créons un mock de TcpStream
qui implémente ces traits.
Implémentons d'abord le trait Read
, qui a une méthode, poll_read
.
Notre mock de TcpStream
va contenir certaines données qui sont copiées dans
le tampon de lecture, et nous allons retourner Poll::Ready
pour signaler que
la lecture est terminée.
use super::*;
use futures::io::Error;
use futures::task::{Context, Poll};
use std::cmp::min;
use std::pin::Pin;
struct MockTcpStream {
donnees_lecture: Vec<u8>,
donnees_ecriture: Vec<u8>,
}
impl Read for MockTcpStream {
fn poll_read(
self: Pin<&mut Self>,
_: &mut Context,
tampon: &mut [u8],
) -> Poll<Result<usize, Error>> {
let taille: usize = min(self.donnees_lecture.len(), tampon.len());
tampon[..taille].copy_from_slice(&self.donnees_lecture[..taille]);
Poll::Ready(Ok(taille))
}
}
Notre implémentation de Write
y ressemble beaucoup, même si nous avons besoin
d'écrire trois méthodes : poll_write
, poll_flush
, et poll_close
.
poll_write
va copier toutes les données d'entrée dans le mock de TcpStream
,
et retourne Poll::Ready
lorsqu'elle sera terminée.
Il n'y a pas besoin de purger et fermer le mock de TcpStream
, donc
poll_flush
et poll_close
peuvent simplement retourner Poll::Ready
.
impl Write for MockTcpStream {
fn poll_write(
mut self: Pin<&mut Self>,
_: &mut Context,
tampon: &[u8],
) -> Poll<Result<usize, Error>> {
self.donnees_ecriture = Vec::from(tampon);
Poll::Ready(Ok(tampon.len()))
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Error>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Error>> {
Poll::Ready(Ok(()))
}
}
Enfin, notre mock a besoin d'implémenter Unpin
, ce qui veut dire que sa
position dans la mémoire peut être déplacée en toute sécurité. Pour plus
d'informations sur l'épinglage et le trait Unpin
, rendez-vous à la section
sur l'épinglage.
use std::marker::Unpin;
impl Unpin for MockTcpStream {}
Maintenant nous sommes prêts à tester la fonction gestion_connexion
.
Après avoir réglé le MockTcpStream
pour contenir les données initiales, nous
pouvons exécuter gestion_connexion
en utilisant l'attribut
#[async_std::test]
, de la même manière que nous avons utilisé
#[async_std::main]
.
Pour nous assurer que gestion_connexion
fonctionne comme attendu, nous
allons vérifier que les données ont été correctement écrites dans le
MockTcpStream
en fonction de son contenu initial.
use std::fs;
#[async_std::test]
async fn test_gestion_connexion() {
let octets_entree = b"GET / HTTP/1.1\r\n";
let mut contenu = vec![0u8; 1024];
contenu[..octets_entree.len()].clone_from_slice(octets_entree);
let mut flux = MockTcpStream {
donnees_lecture: contenu,
donnees_ecriture: Vec::new(),
};
gestion_connexion(&mut flux).await;
let mut tampon = [0u8; 1024];
flux.read(&mut tampon).await.unwrap();
let contenu_attendu = fs::read_to_string("hello.html").unwrap();
let reponse_attendue = format!("HTTP/1.1 200 OK\r\n\r\n{}", contenu_attendu);
assert!(flux.donnees_ecriture.starts_with(reponse_attendue.as_bytes()));
}