Je cherchais sur le web comment tester des appels à un service WCF et faire des tests unitaires.

J’ai trouvé des résultats avec des frameworks d’injection de dépendance, mais je préfère vous proposer une version sans. Le seul framework utilisé sera Moq.

Tout d‘abord voyons le service WCFService qui dérive de IWcfService et qui contient une méthode GetData(int value).

Voici le code du service :

namespace WcfService
{
    public class WcfService : IWcfService
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }
    }
}

Le client Wcf WcfClient consomme WCFService au travers de la classe WcfServiceAgent. Cet agent implémente une méthode HitWcfService faisant appel à GetData.

Voici ce que vous pourriez écrire pour cela :

namespace WcfClient
{
    public class WcfServiceAgent
    {
        public string HitWCFService()
        {
            int val = 1;
            string retVal = string.Empty;

            IWcfService wcfService = new WcfServiceClient(); 

            retVal = wcfService.GetData(val);

            return retVal;
        }
    }
}

Comme vous pouvez le constater il est difficile de faire un test unitaire pour cette fonction.

Modifions le code pour utiliser l’injection de dépendance via le constructeur. Nous allons passer en paramètre l’interface IWcfService.

namespace WcfClient
{
    public class WcfServiceAgent
    {
        IWcfService wcfService;

        public WcfServiceAgent(IWcfService wcfService)
        {
            this.wcfService = wcfService;
        }

        public string HitWCFService()
        {
            int val = 1; string retVal = string.Empty;

            retVal = this.wcfService.GetData(val);

            return retVal;
        }
    }
}

Rien de très compliqué mais nous allons pouvoir utiliser Moq pour créer un test pour cette fonction.

namespace WcfClientTest
{
    [TestClass]
    public class WcfClientUnitTest
    {
        [TestMethod]
        public void MockWcfService()
        {
            string val = "MockedValue";
            string actualRetVal;

            // Initialisation du Mock du service WCF
            Mock<IWcfService> wcfMock = new Mock<IWcfService>();

            // Initialisation de la fonction GetData(int)
            wcfMock.Setup<string>(s=> s.GetData(It.IsAny<int>())).Returns(val);

            // Définition de l'objet Mock
            IWcfService wcfMockObject = wcfMock.Object;

            // Initialisation de l'agent avec le Mock du service
            WCFServiceAgent serviceAgent = new WCFServiceAgent(wcfMockObject);

            // Appel de la fonction
            actualRetVal = serviceAgent.HitWCFService();

            // Vérification des résultats
            wcfMock.Verify(s => s.GetData(It.IsAny<int>()), Times.Exactly(1));
            Assert.AreEqual("MockedValue", actualRetVal, "Not same.");
        }
    }
}

Dans l’exemple ci-dessus nous avons créé un Mock de l’interface IWcfService :

Mock<IWcfService>wcfMock = new Mock<IWcfService>();

Juste pour rappel : un Mock ne peut pas être créé à partir d’une classe concrète. Le code suivant n’est pas possible par exemple :

Mock<WcfService> wcfMock = new Mock<WcfService>().wcfMock.Setup<string>(s => s.GetData(It.IsAny<int>())).Returns(val);

En initialisant wcfMock dans l’exemple nous informons MOQ que nous souhaitons que la méthode GetData renvoi le résultat val quand elle sera appelée.

L’appel suivant vérifie si l’appel de la méthode a eu lieu ou non :

wcfMock.Verify(s => s.GetData(It.IsAny<int>()),Times.Exactly(1));

Nous aurions pu écrire :

wcfMock.Verify(s => s.GetData(4), Times.Exactly(1));

mais nous aurions du définir la méthode ainsi :

wcfMock.Setup<string>(s => s.GetData(4)).Returns(val);

Cependant cela va créer un problème si le paramètre passé à la méthode est créé ou initialisé dans la méthode pour laquelle nous créons le test unitaire. J’ai volontairement créé le paramètre val (val = 1) dans HitWcfService. Moq considère que les 2 méthodes sont différentes (GetData(4) at GetData(1) ne sont pas les mêmes) et quand vous vérifiez cela vous avec une erreur de ce genre :

Moq.MockException: Expected invocation on the mock exactly 1 times, but was 0 times: s => s.GetData(4)

Si vous ne souhaitez pas que Moq compare les paramètre utilisé, utilisez : It.IsAny(). Cela oblige Moq a ignoré les paramètres attendus par ma méthode mockée. Source

Sources