Archives du mot-clé C#

.NET : Génération de jeu de données avec Nbuilder et Faker.Net

Nous avons parfois besoin de jeu de données factices pour nos tests unitaires, peupler une base de données ou pour simuler un service externe par exemple. Il peut être complexe et assez lourd de créer des jeu de données. Je vais vous présenter NBuilder et Faker.Net qui sont 2 librairies pour vous aider à générer vos jeux de données. Ces 2 librairies sont disponibles sous forme de package NuGet.

NBuilder

NBuilder est une librairie qui permet de créer facilement et rapidement des objets, basé sur le design pattern builder. Il va renseigner automatiquement pour vous les propriétés publiques de vos objets. Vous pouvez bien entendu préciser comment générer certaines propriétés.
Par défaut, NBuilder crée des valeur séquentielle dans le cas des liste (Name1, Name2, …)
NBuilder fournit une classe GetRandom qui permet de générer des données aléatoire pour un certains type de données (url, nom , pays, …).

//Création d'un objet
var product = Builder<Product>
    .CreateNew()
        .With(x => x.Name = "Nexus 5")	
	.With(x => x.Price = 199)
    .Build();

//Création d'une liste
var products = Builder<Product>
  .CreateListOfSize(10)
  .All()
  .With(x => x.Name = GetRandom.String(10))	
  .With(x => x.Price = GetRandom.Int(100, 400))
  .Build();

NBuilder fournit également Pick qui permet de sélectionner un objet dans une autre liste

//Sélection d'1 seul élément
Pick<Category>.RandomItemFrom(categories))
//Sélection d'un sous ensemble d'élément
Pick<Category>.UniqueRandomList(With.Between(5).And(10).Elements).From(categories);

Faker.NET

Faker.Net est un bon complément à NBuilder car il permet de générer des valeurs plus réaliste pour les données. Faker.Net contient plus de type de données que NBuilder.

Il contient des générateurs pour:

  • Addresse: (pays, code postal, ville, …)
  • Téléphone
  • Internet (email, password, IP, …)
  • Nom
  • Date
  • Entreprise
  • Finance (Prix, Compte, …)
//Utilisation de Faker.Net avec NBuilder
var customers = Builder<Customer>.CreateListOfSize(100)
        .All()
            .With(c => c.FirstName = Faker.Name.First())
            .With(c => c.LastName = Faker.Name.Last())
            .With(c => c.EmailAddress = Faker.Internet.Email())
            .With(c => c.TelephoneNumber = Faker.Phone.Number())
        .Build();

Un petit exemple d’utilisation avec .Net Fiddle

Liens

.NET : Améliorez vos tests unitaires avec Moq et NFluent

Une des bonnes pratiques d’un projet informatique est la mise en place de tests unitaires. Je vais vous présentez 2 librairies, disponible sous forme de package NuGet, pour vous aidez dans l’écriture de tests.

Moq

Le but d’un test unitaire est de tester un composant isolé, d’où la nécessité d’utiliser une librairie permettant de faire du Mock. J’utilise pour cela Moq.

Les exemples de tests suivant utilisent NUnit comme framework de tests unitaires. Afin de pouvoir facilement remplacer les dépendances des classes à tester, j’utilise Unity comme container d’injection de dépendance (IoC). Vous allez ainsi pouvoir facilement passer à votre classe dans le constructeur votre Mock.

Voyons comment configurer et effectuez des assertions sur vos mock avec Moq :

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using NFluent;
using NUnit.Framework;

namespace Demo.Tests
{
    [TestFixture]
    public class MyServiceTests : BaseServicesTests
    {
        private Mock<IMyService> myServiceMock;
        private Mock<ILogger> loggerMock;
        
        private IMyClass myClass;

        [SetUp]
        public override void SetUp()
        {
            base.SetUp();

            //Create Mocks
            myServiceMock = new Mock<IMyService>();
            loggerMock = new Mock<ILogger>();
                                  
           //We pass our mock to the instance to test
           myClass = new MyClass(myServiceMock, loggerMock);
        }

        [Test]
        public void MyMethodShouldReturnTrue()
        {
            //Setup method on service to return expected value to test our case
            //Here we setup to return an empty list of string
            myServiceMock.Setup(m => m.GetData()).Returns(new List<string>());
            
            var result = myClass.MyMethod();
            //Verify the result is what is expected
            Check.That(result).IsTrue();
            //Verify that our mock has been called
            myServiceMockSetup.Verify(m => m.GetData(), Times.Once);
        }
    }
}

Moq fournit un ensemble de méthodes permettant de configurer (Setup + Returns/Callbacks) et d’effectuer des tests sur les mocks (Verify).
On peut par exemple vérifier le nombre d’appel d’une méthode, les paramètres, …

Voici comment vérifier qu’un méthode prenant un paramètre de type string a bien été appelé 2 fois avec comme valeur de paramètre « MyTestString »

myServiceMock.Verify(r => r.MyMethod(It.Is<string>(x => x == "MyTestString")), Times.Exactly(2));

Le guide de démarrage de Moq

NFluent

Comme vous avez pu peut être le remarquer, je fais pas mal de JavaScript en ce moment et j’aime beaucoup les librairies de tests style jasmine, should, … qui permettent de décrire les assertions de manière plus lisibles. En .Net, j’utilise NFluent qui permet d’avoir une API d’assertion fluent où l’on peut chaîner les assertions à la suite les une des autres.

Check.That(julien)
    .IsNotNull()
    .And.IsNotEqualTo(david)
    .And.IsInstanceOf<Person>();

Dans l’exemple ci-dessus, je vérifie que le l’objet julien n’est pas null, n’est pas égal à david et que cet objet est une instance de la classe Person.

D’autres exemples d’assertions :

//Collections
//Nombre d'élément d'une collection
Check.That(persons).HasSize(3); 
//Il existe une personne ayant la propriété Name égale à Julien
Check.That(persons.Extracting("Name")).Contains("Julien"); 

//Test des exceptions
Check.ThatCode(() => myService.MyMethod())
    .Throws<MyException>()
    .WithMessage("MyMessage")
    .And.WithProperty("MessageKey", "MyMessageKey");

Voici des exemples d’assertion avec NFluent.

Liens

Bon tests 🙂 …

ASP.NET MVC3 : Mise en place du bundling et minification

La minification est une technique permettant de minifier (allégement du poids d’un fichier) et le Bundling, de regrouper des fichiers CSS et JavaScript afin d’optimiser les performances d’un site Web en limitant les appels aux serveurs.

Il faut savoir que votre navigateur va effectuer un appel au serveur pour chaque fichier de votre page. Plus le nombre sera élevé, plus le chargement de votre page sera long. Qui plus est, un navigateur possède une limite concernant le nombre de téléchargement de fichiers simultanés pour un même domaine. Ainsi, une fois cette limite attente, les fichiers suivant devront attendre la fin du chargement des fichiers précédents. Cette limite est actuellement à 6 pour la majorité des navigateurs.

La minification et le bundling vont permettre d’améliorer le temps de chargement de vos pages. MVC4 inclut la bibliothèque permettant de mettre en place ces techniques. Voyons comment l’installer sur un projet MVC3.

Installation de la librairie de Bundling

Il faut installer le package Microsoft ASP.NET Web Optimization Framework via le gestionnaire de paquet NuGet, qui se chargera d’installer les dépendances du package.

Il est posssible de l’installer via la console Nuget soit via le gestionnaire graphique de NuGet.

Via la console

Ouvrir la console Nuget

Lancer la commande :

Install-Package Microsoft.AspNet.Web.Optimization -Pre

Via le gestionnaire graphique

Il faut lancer le gestionnaire graphique en faisant un clic droit sur le projet ASP.NET et cliquer sur Gérer les packages NuGet ….

Lancement du gestionnaire NuGet

NuGetPackager-MVCOptimization

Le paquet étant actuellement en version alpha, il faut sélectionner « Inclure les versions préliminaires » et effectuer une recherche avec le terme Optimization. Il faut ensuite installer le paquet Microsoft ASP.NET Web Optimization Framework.

Utilisation

Il est possible de définir nos bundles via des bundles static. (Il existait auparavant une méthode permettant de créer des bundles de manières automatique via des conventions qui a été à priori supprimée).

On peut ainsi définir un bundle en ajoutant les fichiers 1 par 1 ou par dossier :

Création d’un bundle pour un fichier

ScriptBundle jQueryBundle = new ScriptBundle("~/bundles/jquery");
jQueryBundle.Include("~/Scripts/jquery-1.8.3.js");
bundles.Add(jQueryBundle);

Création d’un bundle pour un dossier avec un pattern sur les fichiers à sélectionner

StyleBundle cssBundle = new StyleBundle("~/Content/css");
cssBundle.IncludeDirectory("~/Content/", "*.css", false);
bundles.Add(cssBundle);

Les bundles doivent être ajoutés dans BundleTable.Bundles

Le plus simple est de définir une classe BundleConfig dans le dossier AppStart qui permet de gérer les bundles comme cela est fait par défaut dans MVC4, ce qui facilitera une migration. Voici le corps de la classe

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Optimization;

namespace MonApplication.MVCApplication.App_Start
{
    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            ScriptBundle jQueryBundle = new ScriptBundle("~/bundles/jquery");
            jQueryBundle.Include("~/Scripts/jquery-1.8.3.js");
            bundles.Add(jQueryBundle);

            // ....
        }
    }
}

Il faut ensuite rajouter dans la méthode Application_Start du fichier Global.asax.cs l’appel à la méthode d’enregistrement des bundles via le code suivant

BundleConfig.RegisterBundles(BundleTable.Bundles);

Déclaration dans le HTML

Il faut ajouter le using suivant (en razor)

@using System.Web.Optimization

Il faut ensuite utiliser les helpers mis à disposition

@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/jquery")

Il faut savoir que par défaut, l’optimisation n’est pas activée par défaut en debug (mais en release oui). Vous pouvez l’activer via :

BundleTable.EnableOptimizations = true;

Enfin, si vous avez déjà des fichiers minifiés (.min) dans votre projet, il faut déclarer les versions non minifiées dans le bundle, l’optimiseur chargera de lui même les fichiers .min de façon transparente en fonction si l’optimisation est activée ou non.

Allez plus loin

Vous pouvez créer votre propre transformation en implémentant l’interface IBundleTransform. Sachez qu’il existe déjà des implémentations pour LESS, SASS, CoffeeScript, TypeScript, … disponible sous forme de package NuGet. Il est également possible d’inclure des librairies JavaScript via des CDN avec support local. Pour en savoir plus, je vous invite à lire cet article sur le site officiel de asp.net qui présente cette fonctionnalité pour MVC4 mais qui est valable après installation du package pour les projet MVC3.

ASP.NET MVC : Etendre un Helper

ASP.NET MVC fournit par défaut des Helpers afin de générer du code HTML facilement. Il existe ainsi des helper pour la gestion des URLs, des inputs de formulaires, …

Une des bonnes pratiques est de créer des helpers perso afin de ne pas mettre de code C# directement dans la vue.

J’ai été confronté récemment dans mon projet à étendre un helper inclus de base dans ASP.NET MVC, celui des dropdownlist. En effet, je devais rendre éditable une liste select en fonction de mon modèle. Afin d’éviter d’avoir du code spaghetti dans ma page vue cshtml, j’ai étendu le Helper fournit par défaut afin de lui passer un paramètre supplémentaire, un booléen canEdit déterminant le statut éditable ou non de mon select.

Il existe 6 signatures pour la méthode DropDownListFor pour lesquelles j’ai ajouté mon paramètre. J’ai été confronté à une difficulté pour une des signatures qui passe en paramètres les attributs html sous la forme d’un objet. Il existe une méthode statique AnonymousObjectToHtmlAttributes qui permet de transformer votre objet en un dictionnaire (RouteValueDictionary) et il devient alors aisé de rajouter un attribut à votre select.

var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
attrs.Add("disabled", "disabled");

Le code complet (sans les commentaires API)

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, SelectList selectList, bool canEdit)
{
	if (canEdit)
	{
		return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList);
	}
	var htmlAttributes = new Dictionary<string, object>();
	htmlAttributes.Add("disabled", "disabled");
	return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, htmlAttributes);
}

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, SelectList selectList, string optionLabel, bool canEdit)
{
	if (!canEdit)
	{
		return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, optionLabel);
	}
	var htmlAttributes = new Dictionary<string, object>();
	htmlAttributes.Add("disabled", "disabled");
	return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, optionLabel, htmlAttributes);
}

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, SelectList selectList, string optionLabel, IDictionary<string, object> htmlAttributes, bool canEdit)
{
	if (!canEdit)
	{
		htmlAttributes.Add("disabled", "disabled");
	}
	return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, optionLabel, htmlAttributes);
}

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, SelectList selectList, IDictionary<string, object> htmlAttributes, bool canEdit)
{
	if (!canEdit)
	{
		htmlAttributes.Add("disabled", "disabled");
	}
	return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, htmlAttributes);
}


public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, SelectList selectList, object htmlAttributes, bool canEdit)
{
	if (canEdit)
	{
		return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, htmlAttributes);
	}
	var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
	attrs.Add("disabled", "disabled");
	return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, attrs);
}

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, SelectList selectList, string optionLabel, object htmlAttributes, bool canEdit)
{
	if (canEdit)
	{
		return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, optionLabel, htmlAttributes);
	}
	var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
	attrs.Add("disabled", "disabled");
	return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, optionLabel, attrs);
}

Et un petit exemple d’utilisation

@Html.DropDownListFor(x => x.MyPropId, new SelectList(Model.MyValues, "ValueId", "ValueName"), Model.User.IsAdmin || Model.User.HasEditRight)