J'utilise Moq & NUnit comme un cadre de test unitaire.Comment moq un NetworkStream dans un test unitaire?
J'ai écrit une méthode qui est donnée d'un objet NetworkStream comme paramètre:
public static void ReadDataIntoBuffer(NetworkStream networkStream, Queue dataBuffer)
{
if ((networkStream != null) && (dataBuffer != null))
{
while (networkStream.DataAvailable)
{
byte[] tempBuffer = new byte[512];
// read the data from the network stream into the temporary buffer
Int32 numberOfBytesRead = networkStream.Read(tempBuffer, 0, 512);
// move all data into the main buffer
for (Int32 i = 0; i < numberOfBytesRead; i++)
{
dataBuffer.Enqueue(tempBuffer[i]);
}
}
}
else
{
if (networkStream != null)
{
throw new ArgumentNullException("networkStream");
}
if (dataBuffer != null)
{
throw new ArgumentNullException("dataBuffer");
}
}
}
Maintenant, je suis à la recherche récrire mes tests unitaires pour cette méthode puisque les tests déjà écrits reposent sur NetworkStream réel objets et ne sont pas très agréable à manipuler.
Comment puis-je simuler le NetworkStream? J'utilise Moq comme mentionné précédemment. Est-ce possible? Sinon, comment pourrais-je contourner ce problème?
Dans l'attente de vos commentaires!
Voici la solution précédente:
public static void ReadDataIntoBuffer(Stream dataStream, Queue dataBuffer)
{
if ((networkStream != null) && (dataBuffer != null))
{
byte[] tempBuffer = new byte[512];
Int32 numberOfBytesRead = 0;
// read the data from the network stream into the temporary buffer
while ((numberOfBytesRead = dataStream.Read(tempBuffer, 0, 512) > 0)
{
// move all data into the main buffer
for (Int32 i = 0; i < numberOfBytesRead; i++)
{
dataBuffer.Enqueue(tempBuffer[i]);
}
}
}
else ...
}
MISE À JOUR:
J'ai réécrites ma classe une fois de plus. Les tests unitaires utilisant la solution précédente se sont bien passés mais l'exemple d'application réel m'a montré pourquoi il n'est pas possible pour moi d'utiliser la suggestion (sinon géniale) de passer un objet Stream
dans ma méthode.
Tout d'abord, mon application repose sur une connexion TCP constante. Si vous utilisez Stream.Read
(ce qui est possible) et qu'il n'y a pas de données à recevoir, cela bloque l'exécution. Si vous spécifiez un délai d'expiration, une exception sera levée si aucune donnée n'est reçue. Ce genre de comportement n'est pas acceptable pour l'application (plutôt simple) dont j'ai besoin. J'ai juste besoin d'une connexion TCP constante et sans fioritures. Par conséquent, avoir la propriété NetworkStream.DataAvailable
est primordiale pour mon implémentation.
La solution actuelle :
Je fini par écrire une interface et une enveloppe à NetworkStream. J'ai également fini par passer le tableau d'octets pour le tampon de réception temporaire dans la méthode. L'unité de test fonctionne maintenant plutôt bien.
public static void ReadDataIntoBuffer(INetworkStream networkStream, Queue dataBuffer, byte[] tempRXBuffer)
{
if ((networkStream != null) && (dataBuffer != null) && (tempRXBuffer != null))
{
// read the data from the network stream into the temporary buffer
while(networkStream.DataAvailable)
{
Int32 numberOfBytesRead = networkStream.Read(tempRXBuffer, 0, tempRXBuffer.Length);
// move all data into the main buffer
for (Int32 i = 0; i < numberOfBytesRead; i++)
{
dataBuffer.Enqueue(tempRXBuffer[i]);
}
}
}
else ...
}
Et voici le test unitaire que j'utilise:
public void TestReadDataIntoBuffer()
{
var networkStreamMock = new Mock<INetworkStream>();
StringBuilder sb = new StringBuilder();
sb.Append(_testMessageConstant1);
sb.Append(_testMessageConstant2);
sb.Append(_testMessageConstant3);
sb.Append(_testMessageConstant4);
sb.Append(_testMessageConstant5);
// ARRANGE
byte[] tempRXBuffer = Encoding.UTF8.GetBytes(sb.ToString());
// return true so that the call to Read() is made
networkStreamMock.Setup(x => x.DataAvailable).Returns(true);
networkStreamMock.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Callback(() =>
{
// after the call to Read() re-setup the property so that we
// we exit the data reading loop again
networkStreamMock.Setup(x => x.DataAvailable).Returns(false);
}).Returns(tempRXBuffer.Length);
Queue resultQueue = new Queue();
// ACT
ReadDataIntoBuffer(networkStreamMock.Object, resultQueue, tempRXBuffer);
// ASSERT
Assert.AreEqual(Encoding.UTF8.GetBytes(sb.ToString()), resultQueue.ToArray());
}
Pouvez-vous changer le 'NetworkStream' dans un' Stream'? Cela pourrait rendre les choses plus faciles. –
Je suppose que je pourrais le faire. Pouvez-vous expliquer comment cela faciliterait les choses? Est-ce que Moq peut se moquer d'un objet Stream? –
Oui, Moq peut se moquer d'un flux car il s'agit d'une classe abstraite, cependant 'Stream' ne contient pas la propriété' DataAvailable', donc je vous suggère d'utiliser l'approche que j'ai décrite ci-dessous. –