Injecter automatiquement des fakes dans une test fixture avec FakeItEasy

Très mauvaisMauvaisMoyenBonExcellent (Pas encore de note) 
Loading...Loading...

Aujourd’hui j’aimerais partager avec vous une fonctionnalité sympa de FakeItEasy que j’ai découverte récemment.

Quand on écrit des tests unitaires pour une classe qui a des dépendances, on a généralement besoin de créer des objets factices (fakes) pour les dépendances et de les injecter manuellement dans la classe testée (SUT, System Under Test), ou d’utiliser un conteneur IoC pour enregistrer les dépendances factices et construire le SUT. C’est un peu laborieux, et il y a quelques mois j’avais créé une extension Unity pour créer automatiquement les fakes et rendre tout ça plus facile. Mais maintenant je réalise que FakeItEasy offre une solution encore meilleure : déclarer simplement les dépendances et le SUT comme des propriétés de la fixture, et appeler Fake.InitializeFixture sur la fixture pour les initialiser. Voilà à quoi ça ressemble :

public class BlogManagerTests
{
    [Fake] public IBlogPostRepository BlogPostRepository { get; set; }
    [Fake] public ISocialNetworkNotifier SocialNetworkNotifier { get; set; }
    [Fake] public ITimeService TimeService { get; set; }
 
    [UnderTest] public BlogManager BlogManager { get; set; }
 
    public BlogManagerTests()
    {
        Fake.InitializeFixture(this);
    }
 
    [Fact]
    public void NewPost_should_add_blog_post_to_repository()
    {
        var post = A.Dummy<BlogPost>();
 
        BlogManager.NewPost(post);
 
        A.CallTo(() => BlogPostRepository.Add(post)).MustHaveHappened();
    }
 
    [Fact]
    public void PublishPost_should_update_post_in_repository_and_publish_link_to_social_networks()
    {
        var publishDate = DateTimeOffset.Now;
        A.CallTo(() => TimeService.Now).Returns(publishDate);
 
        var post = A.Dummy<BlogPost>();
 
        BlogManager.PublishPost(post);
 
        Assert.Equal(BlogPostStatus.Published, post.Status);
        Assert.Equal(publishDate, post.PublishDate);
 
        A.CallTo(() => BlogPostRepository.Update(post)).MustHaveHappened();
        A.CallTo(() => SocialNetworkNotifier.PublishLink(post)).MustHaveHappened();
    }
}

Le SUT est déclaré comme une propriété avec l’attribut [UnderTest]. Chaque dépendance qu’on a besoin de manipuler est déclarée comme une propriété avec l’attribut [Fake]. La méthode Fake.InitializeFixture initialise le SUT, en créant les dépendances factices à la volée, et affecte ces dépendances aux propriétés correspondantes.

J’aime beaucoup le fait que les tests semblent beaucoup plus propres avec cette technique ; le code de plomberie est réduit au strict minimum, il ne reste plus qu’à configurer les dépendances et écrire les tests proprement dits.

Quelques remarques sur le code ci-dessus :

  • On peut utiliser des champs privés plutôt que des propriétés publiques pour les fakes et le SUT, mais comme ces champs ne sont jamais explicitement assignés dans le code, cela provoque un avertissement du compilateur (CS0649), donc je préfère utiliser des propriétés.
  • Les tests dans mon exemple utilisent xUnit, j’ai donc mis l’appel à Fake.InitializeFixture dans le constructeur, mais si vous utilisez un autre framework comme NUnit ou MSTest, vous pouvez le mettre dans la méthode Setup.

Notez aussi qu’il y a des limites aux scénarios supportés par cette approche :

  • seule l’injection par constructeur est supportée, pas l’injection par propriétés (autrement dit les dépendances doivent être des paramètres du constructeur du SUT)
  • les dépendances nommées ne sont pas supportées ; seul le type est pris en compte, on ne peut donc pas avoir plusieurs dépendances distinctes du même type
  • les collections de dépendances ne sont pas supportées (c’est à dire si votre SUT reçoit une collection de dépendances, par exemple IFooService[])

Utiliser plusieurs sources d’annulation avec CreateLinkedTokenSource

Très mauvaisMauvaisMoyenBonExcellent (Pas encore de note) 
Loading...Loading...

La programmation asynchrone en C# était auparavant quelque chose de difficile ; grâce à la Task Parallel Library de .NET 4 et au async/await de C# 5, elle est devenu relativement facile, et en conséquence, est de plus en plus couramment utilisée. Dans le même temps, une approche standardisée pour gérer l’annulation a été introduite : les jetons d’annulation. L’idée générale est que vous créez un CancellationTokenSource qui contrôle l’annulation, et vous passez le jeton qu’il fournit à la méthode que vous voulez pouvoir annuler. Cette méthode le passera ensuite aux autres méthodes qu’elle appelle si elles peuvent être annulées, et/où vérifiera régulièrement si l’annulation a été demandée. Lors de l’annulation, la méthode lancera une OperationCanceledException. Exemple vite fait mal fait :

private readonly IBusinessService _businessService;
private CancellationTokenSource _cancellationSource;
private Task _asyncOperation;
 
private void StartAsyncOperation()
{
    if (_asyncOperation != null)
        return;
    var _cancellationSource = new CancellationTokenSource();
    _asyncOperation = _businessService.DoSomethingAsync(_cancellationSource.Token);
}
 
// async void is bad; like I said, this is a quick and dirty example
private async void StopAsyncOperation()
{
    try
    {
        _cancellationSource.Cancel();
        // wait for the operation to finish
        await _asyncOperation;
    }
    catch (OperationCanceledException)
    {
        // Operation was successfully canceled
    }
    catch (Exception)
    {
        // Oops, something went wrong
    }
    finally
    {
        _asyncOperation = null;
        _cancellationSource.Dispose();
        _cancellationSource = null;
    }
 
...
 
class BusinessService : IBusinessService
{
    public async Task DoSomethingAsync(CancellationToken cancellationToken)
    {
        var data = await GetDataFromServerAsync(cancellationToken);
        foreach (string line in data)
        {
            cancellationToken.ThrowIfCancellationRequested();
            await ProcessLineAsync(line, cancellationToken);
        }
    }
 
    ...
}

Dans ce cas, StopAsyncOperation serait appelée, par exemple, si l’utilisateur décide d’annuler l’opération.

Tout ça marche très bien et est assez facile à mettre en œuvre. Mais que se passe-t-il s’il y a une autre raison d’annuler l’opération, connue seulement du BusinessService et hors du contrôle de la méthode appelante ? C’est là que la méthode CancellationSource.CreateLinkedTokenSource entre en jeu ; en gros, cette méthode crée une nouvelle source d’annulation qui sera annulée quand l’un des jetons spécifiés sera annulé.

Commençons par un cas simple : vous avez un autre jeton d’annulation que vous voulez aussi prendre en compte. Le code pourrait ressembler à ceci :

public async Task DoSomethingAsync(CancellationToken cancellationToken)
{
    var otherToken = GetOtherCancellationToken();
    using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, otherToken))
    {
        var data = await GetDataFromServerAsync(linkedCts.Token);
        foreach (string line in data)
        {
            linkedCts.Token.ThrowIfCancellationRequested();
            await ProcessLineAsync(line, linkedCts.Token);
        }
    }
}

On crée une source d’annulation liée, basée sur les deux jetons d’annulation, et on utilise le jeton de cette nouvelle source à la place de cancellationToken. Si cancellationToken ou otherToken est annulé, linkedCts.Token sera annulé aussi. Si nécessaire, le code appelant peut détecter comment l’opération a été annulée en vérifiant la propriété CancellationToken de l’exception OperationCanceledException.

Maintenant, un cas un peu plus difficile : la seconde source d’annulation est en fait un évènement. On veut annuler l’opération quand cet évènement se produit, en plus de l’annulation par l’utilisateur représentée par le paramètre cancellationToken. On va donc s’abonner à l’évènement et déclencher l’annulation quand il se produit. Voilà un moyen de le faire :

public async Task DoSomethingAsync(CancellationToken cancellationToken)
{
    using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
    {
        EventHandler handler = (sender, e) => linkedCts.Cancel();
        try
        {
            SomeEvent += handler;
            var data = await GetDataFromServerAsync(linkedCts.Token);
            foreach (string line in data)
            {
                linkedCts.Token.ThrowIfCancellationRequested();
                await ProcessLineAsync(line, linkedCts.Token);
            }
        }
        finally
        {
            SomeEvent -= handler;
        }
    }
}

Ici on passe seulement cancellationToken à CreateLinkedTokenSource, et on annule directement linkedCts quand l’évènement se produit. Le code devient un peu plus alambiqué, mais il atteint l’objectif.

Je ne peux pas vraiment vous donner un cas d’utilisation spécifique de cette technique dans le monde réel, parce que les cas où je l’ai utilisée sont trop spécifiques pour être d’intérêt général, mais je peux décrire le scénario général. J’ai une opération de longue durée qui est constituée de plusieurs sous-opérations de longue durée. L’opération entière peut être annulée globalement, et chacune des sous-opérations peut également être annulée individuellement, sans affecter les autres. Voilà à quoi ça peut ressembler (en quasi pseudo-code) :

async Task GlobalOperationAsync(CancellationToken cancellationToken)
{
    foreach (var subOperation is SubOperations)
    {
        cancellationToken.ThrowIfCancellationRequested();
        var subToken = subOperation.GetSpecificCancellationToken();
        using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, subToken))
        {
            try
            {
                await subOperation.RunAsync(linkedCts.Token);
            }
            catch (OperationCanceledException ex)
            {
                // Rethrow only if global cancellation was requested
                if (cancellationToken.IsCancellationRequested)
                    throw;
                     
                // otherwise continue running the other sub-operations
            }
        }
    }
}

Remarquez que, bien que CancellationToken ait été introduit avec la TPL et que tous mes exemples soient asynchrones, vous pouvez également utiliser cette technique avec du code synchrone.

Voilà, j’espère que cela vous sera utile. Passez un bon réveillon du nouvel an, et une excellente année 2016 !

[WPF] Empêcher l’utilisateur de coller une image dans un RichTextBox

Très mauvaisMauvaisMoyenBonExcellent (Pas encore de note) 
Loading...Loading...

Le contrôle RichTextBox de WPF est assez puissant, et très pratique quand on a besoin d’accepter une saisie en texte riche. Cependant, l’une de ses fonctionnalités peut devenir problématique : l’utilisateur peut coller une image. Selon ce qu’on veut faire du texte saisi par l’utilisateur, ce n’est pas forcément souhaitable.

Quand j’ai cherché sur le web un moyen d’empêcher cela, les seules solutions que j’ai trouvées suggéraient d’intercepter la frappe de touches Ctrl-V, et de bloquer l’événement si le presse-papiers contient une image. Cette approche présente plusieurs problèmes:

  • elle n’empêche pas l’utilisateur de coller via le menu contextuel
  • elle ne fonctionne pas si le raccourci de la commande a été modifié
  • elle n’empêche pas l’utilisateur d’insérer une image par glisser-déposer

Puisque cette solution ne me convenait pas, j’ai utilisé le site .NET Framework Reference Source pour chercher un moyen d’intercepter l’opération de collage elle-même. J’ai suivi le code à partir de la propriété ApplicationCommands.Paste, et j’ai finalement trouvé l’événement attaché DataObject.Pasting. Ce n’est pas un endroit où j’aurais pensé à chercher, mais quand on y réfléchit, c’est finalement assez logique. Cet événement peut être utilisé pour intercepter une opération de collage ou de glisser-déposer, et permet au gestionnaire de l’événement de faire différentes choses:

  • annuler purement et simplement l’opération
  • changer le format de données qui sera collé depuis le presse-papiers
  • remplacer le DataObject utilisé pour le collage

Dans mon cas, je voulais juste empêcher le collage ou le glisser-déposer d’une image, donc j’ai simplement annulé l’opération quand le FormatToApply était "Bitmap", comme illustré ci-dessous.

XAML:

<RichTextBox DataObject.Pasting="RichTextBox1_Pasting" ... />

Code-behind:

private void RichTextBox1_Pasting(object sender, DataObjectPastingEventArgs e)
{
    if (e.FormatToApply == "Bitmap")
    {
        e.CancelCommand();
    }
}

Bien sûr, il est également possible de gérer ça plus intelligemment. Par exemple, si le DataObject contient plusieurs formats, on pourrait créer un nouveau DataObject avec uniquement les formats acceptables. Comme ça l’utilisateur peut encore coller quelque chose, tant que ce n’est pas une image.

Posted in WPF. Tags: , , , , . No Comments »

Weak events en C#, suite

Très mauvaisMauvaisMoyenBonExcellent (1 votes) 
Loading...Loading...

Il y a quelques années, j’ai blogué à propos d’une implémentation générique du pattern “weak event” en C#. Le but était de pallier les problèmes de fuites mémoire liés aux évènements quand on oublie de s’en désabonner. L’implémentation était basée sur l’utilisation de références faibles sur les abonnés, de façon à éviter d’empêcher qu’ils soient libérés par le garbage collector.

Ma solution initiale était plus une preuve de concept qu’autre chose, et avait un sérieux problème de performance, dû à l’utilisation de DynamicInvoke à chaque fois que l’évènement était déclenché. Au fil des années, j’ai revisité le problème des “weak events” plusieurs fois, en apportant quelques améliorations à chaque fois, et j’ai maintenant une implémentation qui devrait être suffisamment performante pour la plupart des cas d’utilisation. L’API publique est similaire à celle de ma première solution. En gros, au lieu d’écrire un évènement comme ceci :

public event EventHandler<MyEventArgs> MyEvent;

On l’écrit comme ceci :

private readonly WeakEventSource<MyEventArgs> _myEventSource = new WeakEventSource<MyEventArgs>();
public event EventHandler<MyEventArgs> MyEvent
{
    add { _myEventSource.Subscribe(value); }
    remove { _myEventSource.Unsubscribe(value); }
}

Du point de vue de celui qui s’abonne à l’évènement, c’est exactement pareil qu’un évènement normal, mais l’abonné restera éligible à la garbage collection s’il n’est plus référencé nulle part ailleurs.

L’objet qui publie l’évènement peut le déclencher comme ceci :

_myEventSource.Raise(this, e);

Il y a une petite limitation : la signature de l’évènement doit être EventHandler<TEventArgs> (avec ce que vous voulez comme TEventArgs, bien sûr). Ca ne peut pas être quelque chose comme FooEventHandler, ou un type de délégué custom. Je ne pense pas que ce soit un problème majeur, dans la mesure où une vaste majorité des évènements dans le monde .NET respecte le pattern recommandé void (sender, args), et les delegates spécifiques comme FooEventHandler ont en fait la même signature que EventHandler<FooEventArgs>. J’avais d’abord essayé de supporter n’importe quel type de delegate, mais ça s’est avéré un peu trop compliqué… pour l’instant en tout cas Winking smile.

 

Comment ça marche?

La nouvelle solution est encore basée sur des références faibles, mais change la façon dont la méthode cible est appelée. Au lieu d’utiliser DynamicInvoke, on crée un “open-instance delegate” pour la méthode lors de l’abonnement. Cela signifie que pour une méthode ayant une signature comme void EventHandler(object sender, EventArgs e), on  crée un delegate avec la signature void OpenEventHandler(object target, object sender, EventArgs e). Le paramètre supplémentaire target représente l’instance sur laquelle la méthode est appelée. Pour invoquer le gestionnaire de l’évènement, il suffit de récupérer la cible à partir de la référence faible, et si elle est toujours vivante, de la passer au “open-instance delegate”.

Pour de meilleures performances, ce delegate est en fait créé seulement la première fois qu’on rencontre une méthode donnée, et est mis en cache pour être réutilisé ultérieurement. Ainsi, si plusieurs instances d’une classe s’abonnent à l’évènement avec la même méthode, le delegate ne sera créé que la première fois, et sera réutilisé pour les abonnés suivants.

Notez que techniquement, le delegate créé n’est pas un “vrai” open-instance delegate comme ceux créés par la méthode Delegate.CreateDelegate. Il est en fait créé à l’aide des expressions Linq. La raison est que dans un vrai open-instance delegate, le type du premier paramètre doit être le type qui déclare la méthode, et non object. Puisque cette information n’est pas disponible statiquement, il faut introduire un cast dynamiquement.

 

Le code source est disponible sur GitHub: WeakEvent. Un package NuGet est disponible ici : ThomasLevesque.WeakEvent.

Le dépôt GitHub contient aussi des snippets pour Visual Studio et pour ReSharper, pour faciliter l’écriture du code de plomberie pour un weak event.

Casse-tête C# n°2

Très mauvaisMauvaisMoyenBonExcellent (Pas encore de note) 
Loading...Loading...

Encore un petit casse-tête basé sur un problème que j’ai rencontré au boulot…

Regardez ce morceau de code :

Console.WriteLine($"x > y is {x > y}");
Console.WriteLine($"!(x <= y) is {!(x <= y)}");

Comment faudrait-il déclarer x et y pour que le programme produise la sortie (apparemment illogique) suivante ?

x > y is False
!(x <= y) is True
Posted in Casse-têtes. Tags: , . 5 Comments »

Comment récupérer une date en UTC avec SQLite

Très mauvaisMauvaisMoyenBonExcellent (1 votes) 
Loading...Loading...

SQLite est un super moteur de base de données “in-process” : il est très léger, ne nécessite aucun serveur ni configuration, et fonctionne sur toutes les plateformes. Il y a même un provider ADO.NET officiel très bien réalisé. Cependant, si vous stockez des dates en UTC avec ce provider, vous allez probablement rencontrer un sérieux problème : bien que la date soit correctement stockée en UTC (elle est stockée dans un format similaire à ISO8601, avec un ‘Z’ pour indiquer la zone UTC), quand on la relit depuis la base de données, on obtient un DateTime converti en heure locale, avec Kind = Unspecified. Voici un exemple qui illustre le problème (en utilisant Dapper dans LINQPad) :

void Main()
{
    string connectionString = @"Data Source=D:\tmp\testSQLiteDate.db";
    using (var connection = new SQLiteConnection(connectionString))
    {
        connection.Open();
        connection.Execute("create table Foo(Id integer not null primary key, CreationDateUtc datetime not null)");
        
        var foo = new Foo{Id = 42, CreationDateUtc = DateTime.UtcNow};
        foo.Dump("Original object");
        connection.Execute("insert into Foo(Id, CreationDateUtc) values (@Id, @CreationDateUtc)", foo);
        var foo2 = connection.Query<Foo>("select * from Foo where Id = @Id", new{ Id = 42 }).SingleOrDefault();
        foo2.Dump("After reading from DB");
    }
}

class Foo
{
    public int Id { get; set; }
    public DateTime CreationDateUtc { get; set; }
    public DateTimeKind Kind { get { return CreationDateUtc.Kind; } }
}

Voilà le résultat :

image_thumb[1]

Comme vous pouvez le voir, après avoir relu les données depuis la BDD, la date n’est plus en UTC, et il n’y rien qui indique qu’il s’agit d’une date locale. Cela peut causer toutes sortes de bugs si votre code compare des dates qui sont supposées être en UTC.

J’ai d’abord essayé de résoudre le problème dans mon code, en convertissant explicitement la date en UTC, avant de réaliser que la solution était beaucoup plus simple (quoique pas très bien documentée) : il existe un paramètre de la chaine de connexion pour contrôler comment les dates sont gérées. Il suffit de spécifier DateTimeKind=Utc dans la chaine de connexion :

string connectionString = @"Data Source=D:\tmp\testSQLiteDate.db;DateTimeKind=Utc";

Si vous utilisez SqliteConnectionStringBuilder pour construire la chaine de connexion, il faut définir la propriété DateTimeKind à DateTimeKind.Utc.

Le type de date est maintenant préservé :

image_thumb[2]

 

Mise en garde importante : ce paramètre s’applique à toutes les dates lues depuis cette connexion. Si vous avez des dates qui n’étaient pas stockées en UTC, le provider les considèrera quand-même comme UTC, ce qui produira des résultats incorrects. N’utilisez donc ce paramètre que si toutes vos dates sont stockées en UTC.

Filtres d’exception en C# 6 : leur plus grand avantage n’est pas celui qu’on croit

Très mauvaisMauvaisMoyenBonExcellent (Pas encore de note) 
Loading...Loading...

Les filtres d’exception sont l’une des fonctionnalités majeures de C# 6. Ils tirent parti d’une fonctionnalité du CLR qui a toujours existé, mais qui n’était pas exploitée en C# jusqu’ici. Ils permettent de spécifier une condition sur un bloc catch :

static void Main()
{
    try
    {
        Foo.DoSomethingThatMightFail(null);
    }
    catch (MyException ex) when (ex.Code == 42)
    {
        Console.WriteLine("Error 42 occurred");
    }
}

Comme on pourrait s’y attendre, le bloc catch ne sera exécuté que si ex.Code == 42. Si cette condition n’est pas vérifiée, l’exception sera propagée en remontant la pile jusqu’à ce qu’elle soit interceptée ailleurs ou qu’elle termine le processus.

A première vue, cela n’apporte rien de très nouveau. Après tout, on pouvait déjà faire ceci :

static void Main()
{
    try
    {
        Foo.DoSomethingThatMightFail(null);
    }
    catch (MyException ex)
    {
        if (ex.Code == 42)
            Console.WriteLine("Error 42 occurred");
        else
            throw;
    }
}

Puisque ce bout de code est équivalent au précédent, les filtres d’exception sont juste du sucre syntaxique… enfin, ils sont équivalents, non ?

NON !

Déroulement de pile

Il y a en fait une différence subtile mais importante : les filtres d’exception ne déroulent pas la pile. OK, mais qu’est-ce que ça veut dire ?

Quand on entre dans un bloc catch, la pile est déroulée : cela signifie que toutes les frames pour les appels de méthode plus “profonds” que la méthode courante sont dépilées. Cela implique que toutes les informations concernant l’état d’exécution de ces méthodes sont perdues, ce qui rend plus difficile de trouver la cause première de l’exception.

Supposons que la méthode DoSomethingThatMightFail lance une MyException avec le code 123, et que le débogueur soit configuré pour ne s’arrêter que sur les exceptions non gérées.

  • Dans le code sans filtre d’exception, on entre toujours dans le bloc catch (puisque le type de l’exception correspond), et la pile est immédiatement déroulée. Puisque l’exception ne satisfait pas la condition, elle est relancée. Le débogueur s’arrêtera donc sur le throw; dans le block catch ; aucune information sur l’état d’exécution de la méthode DoSomethingThatMightFail ne sera disponible. Autrement dit, on ne pourra pas savoir ce qui était en train de se passer dans la méthode qui a lancé l’exception.
  • En revanche, dans le code qui utilise un filtre d’exception, l’exception ne satisfait pas la condition, donc on n’entrera pas du tout dans le bloc catch, et la pile ne sera pas déroulée. Le débogueur s’arrêtera dans la méthode DoSomethingThatMightFail, ce qui permettra de voir facilement ce qui était en train de se passer quand l’exception a été lancée.

Bien sûr, quand on débogue directement une application dans Visual Studio, on peut configurer le débogueur pour s’arrêter dès qu’une exception est lancée, qu’elle soit gérée ou non. Mais on n’a pas toujours cette possibilité ; par exemple, si on débogue un problème en production, on travaille plutôt sur un crash dump, donc le fait que la pile n’ait pas été déroulée devient très utile, puisque ça permet de voir ce qui était en train de se passer dans la méthode qui a lancé l’exception.

Pile vs. trace de pile

Vous avez peut-être remarqué que j’ai parlé plus haut de la pile (call stack), et non de la trace de pile (stack trace). Bien qu’on utilise souvent le terme “pile” pour faire référence à la trace de pile, ce sont deux choses bien différentes. La pile est une zone mémoire allouée à chaque thread qui contient des informations sur les méthodes en cours d’exécution : adresse de retour, arguments, et variables locales. La trace de pile est juste une chaine qui contient les noms des méthodes actuellement sur la pile (et l’emplacement dans ces méthodes, si les symboles de débogage sont disponibles). La propriété Exception.StackTrace contient la trace de la pile telle qu’elle était quand l’exception a été lancée, et n’est pas affectée quand la pile est déroulée ; si on relance l’exception avec throw;, elle n’est pas modifiée non plus. Elle n’est écrasée que si on relance l’exception avec throw ex;. La pile elle-même, en revanche, est déroulée quand on entre dans un bloc catch, comme décrit plus haut.

Effets de bord

il est intéressant de noter qu’un filtre d’exception peut contenir n’importe quelle expression qui renvoie un bool (enfin presque… on ne peut pas utiliser await par exemple). Cela peut être une condition logique, une propriété, un appel de méthode, etc. Techniquement, rien n’empêche de causer des effets de bord dans un filtre d’exception. Dans la plupart des cas, je déconseillerais vivement de faire ça, car ça peut causer des comportements très déroutants ; il peut devenir très difficile de comprendre dans quel ordre les choses sont exécutées. Cependant, il y a un scénario courant qui pourrait bénéficier d’effets de bord dans un filtre d’exception: le logging. On pourrait facilement créer une méthode qui log l’exception et renvoie false pour qu’on n’entre pas dans le bloc catch. Cela permettrait de logger les exception à la volée sans les gérer, et donc sans dérouler la pile:

try
{
    DoSomethingThatMightFail(s);
}
catch (Exception ex) when (Log(ex, "An error occurred"))
{
    // this catch block will never be reached
}
 
...
 
static bool Log(Exception ex, string message, params object[] args)
{
    Debug.Print(message, args);
    return false;
}

Conclusion

Comme avez pu le voir, les filtres d’exception ne sont pas juste du sucre syntaxique. Contrairement à la plupart des fonctionnalités de C# 6, ce n’est pas vraiment une fonctionnalité de “codage” (dans le sens où ça ne rend pas le code significativement plus clair), mais plutôt une fonctionnalité de “débogage”. Bien compris et utilisés, ils peuvent rendre beaucoup plus facile la résolution de problèmes dans le code.

Créer un conteneur qui crée automatiquement des objets factices avec Unity et FakeItEasy

Très mauvaisMauvaisMoyenBonExcellent (Pas encore de note) 
Loading...Loading...

L’écriture de tests unitaires peut parfois être un peu rébarbative, surtout quand on teste des classes qui ont des dépendances complexes. Heureusement, certains outils rendent cela un peu plus facile. J’utilise beaucoup FakeItEasy ces derniers temps ; c’est un framework de mock pour .NET très facile à utiliser. Il a une API simple et élégante, basée sur les génériques et les expressions lambda, et c’est un vrai plaisir de l’utiliser. Ça a été une bouffée d’air frais par rapport au vieux RhinoMocks que j’utilisais jusqu’ici.

Mais malgré toutes les qualités de FakeItEasy, l’enregistrement des fakes (objets factices) pour les dépendances de la classe testée est encore un peu laborieuse. Ce serait bien si le conteneur IoC pouvait nous créer automatiquement des fakes à la volée, non ? Le code suivant :

var container = new UnityContainer();
 
// Setup dependencies
var fooProvider = A.Fake<IFooProvider>();
container.RegisterInstance(fooProvider);
var barService = A.Fake<IBarService>();
container.RegisterInstance(barService);
var bazManager = A.Fake<IBazManager>();
container.RegisterInstance(bazManager);
 
var sut = container.Resolve<SystemUnderTest>();

Pourrait être réduit à ceci :

var container = new UnityContainer();
 
// This will cause the container to provide fakes for all dependencies
container.AddNewExtension<AutoFakeExtension>();
 
var sut = container.Resolve<SystemUnderTest>();

Eh bien, c’est en fait assez facile à faire avec Unity. Unity n’est pas vraiment le conteneur IoC le plus à la mode, mais il est bien supporté, facile à utiliser, et extensible. J’ai créé l’extension suivante pour permettre le scénario décrit plus haut :

public class AutoFakeExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        Context.Strategies.AddNew<AutoFakeBuilderStrategy>(UnityBuildStage.PreCreation);
    }
     
    private class AutoFakeBuilderStrategy : BuilderStrategy
    {
        private static readonly MethodInfo _fakeGenericDefinition;
     
        static AutoFakeBuilderStrategy()
        {
            _fakeGenericDefinition = typeof(A).GetMethod("Fake", Type.EmptyTypes);
        }
         
        public override void PreBuildUp(IBuilderContext context)
        {
            if (context.Existing == null)
            {
                var type = context.BuildKey.Type;
                if (type.IsInterface || type.IsAbstract)
                {
                    var fakeMethod = _fakeGenericDefinition.MakeGenericMethod(type);
                    var fake = fakeMethod.Invoke(null, new object[0]);
                    context.PersistentPolicies.Set<ILifetimePolicy>(new ContainerControlledLifetimeManager(), context.BuildKey);
                    context.Existing = fake;
                    context.BuildComplete = true;
                }
            }
            base.PreBuildUp(context);
        }
    }
}

Quelques commentaires sur ce code :

  • La logique est un peu rudimentaire (on génère un fake uniquement pour les interfaces et classes abstraites), mais elle peut facilement être ajustée selon les besoins.
  • Le vilain hack de réflexion est dû au fait que FakeItEasy n’a pas de surcharge non générique pour la méthode A.Fake qui accepte un Type en paramètre. Nul n’est parfait…
  • La durée de vie du fake est définie à “container controlled” (c’est-à-dire qu’elle a la durée de vie du conteneur), parce que si on a besoin de configurer les appels sur le fake, il faut qu’on puisse accéder à la même instance que celle qui est injectée dans la classe testée :
var fooProvider = container.Resolve<IFooProvider>();
A.CallTo(() => fooProvider.GetFoo(42)).Returns(new Foo { Id = 42, Name = “test” });

Remarquez que si on enregistre explicitement une dépendance dans le conteneur, elle sera utilisée en priorité et aucun fake ne sera créé. On peut donc utiliser cette extension tout en gardant la possibilité de spécifier certaines dépendances manuellement :

var container = new UnityContainer();
container.AddNewExtension<AutoFakeExtension>();
container.RegisterType<IFooProvider, TestFooProvider>();
var sut = container.Resolve<SystemUnderTest>();

Bien sûr, cette extension pourrait facilement être modifiée pour utiliser un framework de mock autre que FakeItEasy. Je suppose que le même principe pourrait aussi être appliqué à d’autres conteneurs IoC, pourvu qu’ils aient des points d’extension appropriés.

Et AutoFixture dans tout ça ?

Avant que vous ne posiez la question : oui, je connais AutoFixture. C’est une très bonne librairie, et j’ai aussi essayé de l’utiliser, avec un certain succès. Le code est d’ailleurs très similaire aux exemples ci-dessus. La principale raison pour laquelle je n’ai pas continué à l’utiliser est qu’AutoFixture n’est pas vraiment un conteneur IoC (bien qu’il fasse certaines des choses que fait un conteneur IoC), et je préfère utiliser le même mécanisme d’injection de dépendance dans mes tests unitaires que dans le code applicatif. De plus, je n’étais pas très à l’aise avec la façon dont AutoFixture gère les propriétés quand il crée une instance de la classe à tester. Par défaut, il initialise toutes les propriétés publiques avec des fakes de leur type ; c’est très bien dans le cas des propriétés utilisées pour l’injection de dépendance, mais ça n’a pas de sens pour les autres propriétés. Je sais qu’il est possible d’inhiber ce comportement pour des propriétés spécifiques, mais il faut le faire manuellement au cas par cas ; cela ne prend pas en compte les attributs spécifiques au conteneur IoC, comme [Dependency]. Au final, j’ai trouvé plus simple d’utiliser mon extension Unity personnalisée, dont je comprends et maitrise bien le comportement.

Support de l’asynchronisme et de l’annulation pour les wait handles

Très mauvaisMauvaisMoyenBonExcellent (Pas encore de note) 
Loading...Loading...

Le .NET Framework fournit un certain nombre de primitives de synchronisation bas niveau. Les plus couramment utilisées sont appelées “wait handles”, et héritent de la classe WaitHandle : Semaphore, Mutex, AutoResetEvent et ManualResetEvent. Ces classes existent depuis .NET 2.0 (voire 1.1 pour certaines), mais elles n’ont pas beaucoup évolué depuis, ce qui fait qu’elles ne supportent pas des fonctionnalités introduites plus tard et devenues très courantes. En particulier, elles ne supportent pas l’attente asynchrone, ni l’annulation de l’attente. Heureusement, il est assez facile d’ajouter ces fonctionnalités via des méthodes d’extension.

Annulation

Commençons par le plus facile : l’annulation de l’attente. Dans certains cas, on voudrait pouvoir passer un CancellationToken à WaitHandle.WaitOne, mais aucune des surcharges ne le supporte. Notez que des variantes plus récentes de certaines primitives de synchronisation, comme SemaphoreSlim et ManualResetEventSlim, supportent l’annulation ; cependant elles ne sont pas appropriées dans toutes les situations, car elles sont conçues pour les cas où les temps d’attente sont très courts.

Un CancellationToken expose un WaitHandle, qui est signalé quand l’annulation est demandée. On peut tirer parti de cela pour implémenter l’attente asynchrone sur un wait handle :

public static bool WaitOne(this WaitHandle handle, int millisecondsTimeout, CancellationToken cancellationToken)
{
    int n = WaitHandle.WaitAny(new[] { handle, cancellationToken.WaitHandle }, millisecondsTimeout);
    switch (n)
    {
        case WaitHandle.WaitTimeout:
            return false;
        case 0:
            return true;
        default:
            cancellationToken.ThrowIfCancellationRequested();
            return false; // never reached
    }
}

On utilise WaitHandle.WaitAny pour attendre que le wait handle d’origine ou celui du jeton d’annulation soit signalé. WaitAny renvoie l’index du premier wait handle qui a été signalé, ou  WaitHandle.WaitTimeout si un timeout s’est produit avant que l’un des wait handles ne soit signalé. On a donc 3 résultats possibles :

  • un timeout s’est produit : on renvoie false (comme la méthode WaitOne standard) ;
  • le wait handle d’origine a été signalé en premier : on renvoie true (comme la méthode WaitOne standard) ;
  • le wait handle du jeton d’annulation a été signalé en premier : on lance une OperationCancelledException.

Pour compléter, on peut rajouter quelques surcharges pour les cas d’utilisation courants :

public static bool WaitOne(this WaitHandle handle, TimeSpan timeout, CancellationToken cancellationToken)
{
    return handle.WaitOne((int)timeout.TotalMilliseconds, cancellationToken);
}
 
public static bool WaitOne(this WaitHandle handle, CancellationToken cancellationToken)
{
    return handle.WaitOne(Timeout.Infinite, cancellationToken);
}

Et voilà, on a maintenant une méthode WaitOne qui supporte l’annulation !

Attente asynchrone

Maintenant, comment faire pour attendre un wait handle de façon asynchrone ? C’est un peu plus difficile. Ce qu’on veut ici est une méthode WaitOneAsync qui va renvoyer un Task<bool> (et pendant qu’on y est, autant inclure aussi le support de l’annulation). L’approche habituelle pour créer un wrapper Task pour une opération asynchrone non basée sur une tâche est d’utiliser un TaskCompletionSource<T>, c’est donc ce qu’on va faire. Quand le wait handle sera signalé, on mettra à true le résultat de la tâche; si un timeout se produit, on le mettra à false; et si le jeton d’annulation est signalé, on marquera la tâche comme annulée.

J’ai eu un peu de mal à trouver un moyen d’exécuter un delegate quand un wait handle est signalé, mais j’ai fini par tomber sur la méthode ThreadPool.RegisterWaitForSingleObject, qui sert précisément à ça. Je ne sais pas trop pourquoi elle est dans la classe ThreadPool ; il me semble que ça aurait eu plus de sens de la mettre dans la classe WaitHandle, mais je suppose qu’il y a une bonne raison.

Voilà donc ce qu’on va faire :

  • créer un TaskCompletionSource<bool> ;
  • enregistrer un delegate qui mettra le résultat de la tâche à true quand le wait handle sera signalé, ou à false si un timeout se produit avant, à l’aide de ThreadPool.RegisterWaitForSingleObject ;
  • enregistrer un delegate pour marquer la tâche comme annulée quand le jeton d’annulation sera signalé, en utilisant CancellationToken.Register ;
  • désenregistrer les deux delegates une fois que la tâche est terminée.

Voici l’implémentation :

public static async Task<bool> WaitOneAsync(this WaitHandle handle, int millisecondsTimeout, CancellationToken cancellationToken)
{
    RegisteredWaitHandle registeredHandle = null;
    CancellationTokenRegistration tokenRegistration = default(CancellationTokenRegistration);
    try
    {
        var tcs = new TaskCompletionSource<bool>();
        registeredHandle = ThreadPool.RegisterWaitForSingleObject(
            handle,
            (state, timedOut) => ((TaskCompletionSource<bool>)state).TrySetResult(!timedOut),
            tcs,
            millisecondsTimeout,
            true);
        tokenRegistration = cancellationToken.Register(
            state => ((TaskCompletionSource<bool>)state).TrySetCanceled(),
            tcs);
        return await tcs.Task;
    }
    finally
    {
        if (registeredHandle != null)
            registeredHandle.Unregister(null);
        tokenRegistration.Dispose();
    }
}
 
public static Task<bool> WaitOneAsync(this WaitHandle handle, TimeSpan timeout, CancellationToken cancellationToken)
{
    return handle.WaitOneAsync((int)timeout.TotalMilliseconds, cancellationToken);
}
 
public static Task<bool> WaitOneAsync(this WaitHandle handle, CancellationToken cancellationToken)
{
    return handle.WaitOneAsync(Timeout.Infinite, cancellationToken);
}

Notez que les expressions lambda auraient pu utiliser la variable tcs directement ; cela rendrait le code un peu plus lisible, mais causerait la création d’une closure, donc pour éviter cela et améliorer un peu les performances, on passe tcs via le paramètre state.

On peut maintenant utiliser notre méthode WaitOneAsync de la façon suivante :

var mre = new ManualResetEvent(false);
…
if (await mre.WaitOneAsync(2000, cancellationToken))
{
    …
}

Remarque importante : cette méthode ne fonctionnera pas pour un Mutex, car elle repose sur RegisterWaitForSingleObject, qui d’après la documentation ne fonctionne qu’avec les wait handles autres que Mutex.

Conclusion

On a donc vu qu’avec juste quelques méthodes d’extension, on peut rendre les primitives de synchronisation standard bien plus pratiques à utiliser dans du code moderne qui tire parti de l’asynchronisme et de l’annulation. Cependant je peux difficilement terminer ce billet sans mentionner la librairie AsyncEx de Stephen Cleary ; c’est une boite à outils très complète qui fournit des versions asynchrones de la plupart des primitives de synchronisation, dont certaines qui permettront d’arriver au même résultat que le code ci-dessus. Je vous invite à y jeter un œil, elle contient plein de choses utiles.

Posted in Code sample. Tags: , , . No Comments »

Tirer parti des annotations de ReSharper

Très mauvaisMauvaisMoyenBonExcellent (1 votes) 
Loading...Loading...

Je ne pense pas qu’il soit vraiment nécessaire de présenter ReSharper (souvent abbrégé R#), mais au cas où vous ne connaitriez pas, il s’agit d’un outil créé par JetBrains qui analyse votre code C# ou VB.NET en temps réel pour vous avertir des bugs potentiels, mauvaises pratiques, non-respect des conventions, etc. Il propose aussi de nombreux refactorings et générateurs de code bien utiles. Je l’utilise depuis quelques années maintenant, et cela a beaucoup amélioré ma productivité et la qualité de mon code.

Entre autres choses, R# vous avertit sur l’utilisation incorrecte de méthodes du .NET Framework. Par exemple, si vous appelez Path.GetFullPath avec un chemin qui est peut-être null, il affiche un avertissement :

image

Comment R# sait-il que Path.GetFullPath n’accepte pas d’argument null ? Et d’ailleurs, comment sait-il que Console.ReadLine peut renvoyer null ? Je suppose que ça aurait pu être codé en dur dans l’outil, mais ce ce ne serait pas une approche très élégante, et ne permettrait pas l’extensibilité… En fait, ReSharper utilise des annotations externes. Ce sont des fichiers XML qui sont fournis avec R# et contiennent des métadonnées à propos des classes et méthodes du .NET Framework. Ces données sont utilisées par l’analyseur pour détecter les problèmes potentiels dans votre code.

Soit, mais alors qu’en est-il des bibliothèques tierces ? Evidemment, JetBrains ne peut pas créer les annotations pour celles-ci, il y en a trop. La bonne nouvelle, c’est que vous pouvez écrire vos propres fichiers d’annotations externes pour les bibliothèques que vous utilisez, ou pour votre propre code. Cependant, pour votre code, il y a un moyen beaucoup plus pratique : vous pouvez appliquer les annotations directement dans votre code à l’aide d’attributs. Il y a deux moyens d’obtenir ces attributs :

  • Référencer l’assembly où ils sont définis (JetBrains.Annotations.dll dans le répertoire d’installation de R#). C’est très bien si ça ne vous gêne pas d’avoir une référence vers quelque chose qui n’a rien à voir avec votre application, mais ce n’est probablement pas une bonne idée pour une librairie.
  • Déclarer les attributs dans votre propre code. En fait, il n’est pas nécessaire de les écrire soi-même, car R# a une option pour copier leur implémentation dans le presse-papier, comme illustré ci-dessous. Il suffit ensuite de les coller dans un fichier de code du projet.

image

Maintenant qu’on a les attributs, comment les utilise-t-on ? Je vais présenter quelques unes des annotations les plus fréquemment utilisées.

NotNull

Cette annotation indique que l’élément sur lequel il est appliqué ne peut pas, ou ne doit pas, être null.

Si elle est appliquée sur une méthode ou propriété, cela signifie que celle-ci ne renverra jamais null:

[NotNull]
public string GetString()
{
    return "Hello world!";
}

Quand une méthode a cet attribut, si vous testez que la valeur de retour est null (ou non null), R# vous avertira que la condition est toujours fausse (ou vraie) :

image

 

Si l’annotation est appliquée à un paramètre de méthode, cela signifie que null n’est pas une valeur d’argument valide :

public string Repeat([NotNull] string s)
{
    if (s == null)
        throw new ArgumentNullException("s");
    return s + s;
}

Si R# détermine que la valeur passée pour s peut être null, il vous en avertira, comme illustré dans le premier exemple de cet article.

Cette annotation peut être ajoutée automatiquement en utilisant le menu  “quick fix” de ReSharper. L’option “Not null” va juste ajouter l’annotation; l’option “Check parameter for null” va ajouter l’annotation ainsi qu’une vérification de la valeur:

image

image

 

CanBeNull

C’est le contraire de NotNull. Appliquée à une méthode ou propriété, cette annotation signifie que cette méthode ou propriété peut renvoyer une valeur null. Appliquée à un paramètre, elle signifie que null est une valeur d’argument valide.

Pure

Celle-ci est très utile. Appliquée à une méthode, elle signifie que la méthode est pure. Une méthode pure n’a pas d’effet de bord observable, donc si vous n’utilisez pas la valeur renvoyée par la méthode, l’appel n’a aucun effet utile, c’est donc probablement une erreur. Exemple typique avec String.Replace:

image

 

StringFormatMethod

Cette annotation indique qu’une méthode fonctionne sur le même principe que la méthode String.Format, c’est-à-dire qu’elle prend une chaine de format composite, suivie d’un nombre variable d’arguments qui remplaceront les “trous” dans la chaine de format.

[StringFormatMethod("format")]
public static void Log(string format, params object[] args)
{
    ...
}

Cela permet à R# de vous avertir si les trous et les arguments ne correspondent pas :

image

UsedImplicitly

Celle-ci indique à ReSharper qu’un élément de code est utilisé, même si R# ne peut pas le détecter statiquement. Cela a pour effet d’inhiber l’avertissement qui dit qu’un élément n’est pas utilisé. Cela est utile, par exemple, quand un type ou membre est utilisé uniquement via la réflexion.

NoEnumeration

Cette annotation est appliquée à un paramètre IEnumerable, et signifie que la méthode n’énumèrera pas la séquence. R# donne un avertissement quand un IEnumerable est énuméré plusieurs fois, donc utiliser cet attribut permet d’éviter les faux positifs pour cet avertissement :

public static IEnumerable<T> EmptyIfNull<T>([NoEnumeration] this IEnumerable<T> source)
{
    return source ?? Enumerable.Empty<T>();
}

InstantHandle

Celle-ci s’applique à un paramètre de type delegate, et signifie que le delegate sera exécuté uniquement pendant l’exécution de la méthode. Cela permet d’empêcher l’avertissement “Access to modified closure”, qui apparait quand une expression lambda capture une variable qui est modifiée ultérieurement.

ContractAnnotation

Cette annotation est un moyen puissant de décrire les dépendances entre les entrées et la sortie d’une méthode. Cela permet à R# de prédire la façon dont une méthode se comportera. Par exemple, cette méthode renverra null si son argument est null, et non null sinon :

[ContractAnnotation("null => null; notnull => notnull")]
public object Transform(object data)
{
    ...
}

Grâce à cette annotation, ReSharper saura que si l’argument était non null, le résultat ne sera pas null non plus.

La méthode suivante ne se termine pas normalement (elle lance une exception) si son argument est null:

[ContractAnnotation("value:null => halt")]
public static void CheckArgumentNull<T>(
    [NoEnumeration] this T value,
    [InvokerParameterName] string paramName)
    where T : class
{
    if (value == null)
        throw new ArgumentNullException(paramName);
}

Cela permet à R# de savoir que si vous passez null à cette méthode, le code qui suit l’appel ne sera jamais atteint; s’il est atteint, on peut supposer que la valeur n’est pas null.

LocalizationRequired

Cette annotation signifie qu’une propriété ou un paramètre de méthode devrait être localisé; si vous passez une valeur codée en dur, R# vous avertira et suggèrera de l’extraire vers un fichier de ressource.

image11

Conclusion

Maintenant, vous vous demandez peut-être “mais pourquoi est-ce que je me donnerais tant de mal pour ajouter toutes ces annotations à mon code?”. La raison est simple : cela aide ReSharper à vous aider ! En lui donnant plus d’informations sur votre code, vous permettez à R# de vous donner des recommandations plus pertinentes et de produire moins de faux positifs. De plus, si vous être un auteur de bibliothèque, cela améliore l’utilisation de votre bibliothèque pour les utilisateurs de ReSharper. J’utilise beaucoup les annotations R# dans ma bibliothèque Linq.Extras, c’est donc un bon endroit pour trouver d’autres exemples.

Remarquez que je n’ai décrit qu’une petite partie des annotations disponibles. Il y en a beaucoup d’autres, principalement destinées à des scénarios spécifiques à ASP.NET. Vous pouvez les voir dans le fichier d’annotations généré par ReSharper, ou dans la documentation (qui n’est pas tout à fait complète, mais quand même utile).

css.php