Knowledge-base
  • Home
  • Samman Technical Coaching
  • Software craftsmanship
    • Practices
      • Pair Programming
      • Code Review
      • Co-designs
      • Design sessions
      • Interview Domain Experts
      • Dev ethics
    • The Software Craftsman
    • Egoless Crafting
    • Technical debt workshop
    • Functional Programming made easy in C# with Language-ext
    • F# for OO programmers
    • Domain Modeling Made Functional
    • Testing
      • Clean Tests
      • Improve the design and testing of your micro-services through CDC Tests
        • CDC testing made simple with Pact
        • Pact broker : the missing piece of your Consumer-Driven Contract approach
      • Improve your test quality with Mutation testing
      • How to name our Unit Tests
      • How to write better assertions
    • Katas
      • TDD
        • Stack kata
        • Fizzbuzz
        • Outside-in TDD (London Style)
      • Improve your software quality with Property-Based Testing
        • A journey to Property-Based Testing
      • Clean Code
      • Clean Architecture
      • Write S.O.L.I.D code
      • Mocking
      • Gilded Rose (Approval Testing)
      • Mikado method
        • Mikado kata
      • Pure functions
      • Theatrical players refactoring Kata
        • Let's refactor (OOP style)
        • Let's refactor (FP style)
      • Functional Programming made easy in Java & C#
      • Refactoring journey
      • Refactoring du Bouchonnois
        • 1) Se faire une idée du code
        • 2) "Treat warnings as errors"
        • 3) Let's kill some mutants
        • 4) Améliorer la lisibilité des tests
        • 5) "Approve Everything"
        • 6) Définir des propriétés
        • 7) Tests d'architecture
        • 8) Use Cases
        • 9) Tell Don't Ask
        • 10) "Avoid Primitives" - Commands
        • 11) "Avoid Exceptions"
        • 12) "Event Sourcing"
    • Software Design X-Rays
      • Workshop
    • The Programmer's Brain
      • How to read code better
  • Software Architecture
    • Fundamentals of Software Architecture
    • Aligning Product & Software Design
    • DDD re-distilled
    • Test your architecture with Archunit
    • NoSQL
  • Agile coaching
    • How to run a Community of Practices (COP)
    • The developers — the forgotten of agility
      • The secrets to re-on-board the devs in agility
    • Coaching toolbox
      • Echelle
      • Learning expedition
    • How to improve Team Decision making ?
      • Decision Making Principles and Practices
    • Learning 3.0
    • Retrospectives
      • Back to the Future
      • Mission Impossible
      • Movie themes
      • Rétro dont vous êtes le héros
      • Sad/Mad/Glad
      • Speed boat
      • Star wars theme
      • Story cubes
    • Technical Agile Coaching with the Samman Method
    • Xanpan - a team centric agile method story
    • XTREM WATCH — Découvrez la puissance de la veille collective
    • Become a better speaker through peer feedback
    • Project-to-Product Principles
  • Leadership
    • Bref. J'ai pris une tarte dans la gueule (et ça fait extrêmement de bien)
    • Forward Summit 2020
    • Learn leadership from the Navy SEALs
    • Learn to lead and help your team(s) to be successful
    • Towards a learning organization and beyond
    • Leadership is language
  • Serious games
    • My serious games
    • Libérez vos entretiens d’embauche avec la gamification
    • How to create a game
    • How to debrief a game ?
    • Lego Serious Play (LSP)
      • LSP in your job interviews
  • Xtrem Reading
    • Cultivate Team Learning with Xtrem Reading
    • My Book Infographics
    • How to make book infographics
    • En route vers l’apprenance avec Xtrem Reading
    • Resources
      • Book notes
        • Agile People: A Radical Approach for HR & Managers
        • Agile testing : A Practical Guide for Testers and Agile Teams
        • Boite à outils de l'intelligence émotionnelle
        • Building a better business using Lego Serious Play method
        • Building evolutionary architectures
        • Code that fits in your head
        • Culture Agile
        • Culture is everything
        • Domain-Driven Design: The First 15 Years
        • Dynamic Reteaming - The Art and Wisdom of Changing Teams
        • How to avoid a Climate Disaster
        • La liberté du commandement
        • Réaliser ses rêves, ça s'apprend
        • Refactoring at Scale
        • Succeeding with OKRs in Agile
        • Team Topologies
        • The Good Life
        • Tu fais quoi dans la vie
        • Who Does What By How Much?
  • My Activity
    • Retour sur mon année 2020
Powered by GitBook
On this page
  • Inward Dependencies
  • Domain Model
  • Règle de l'Application Services
  • Quid de l'infrastructure ?
  • Règles d'équipe
  • Reflect

Was this helpful?

  1. Software craftsmanship
  2. Katas
  3. Refactoring du Bouchonnois

7) Tests d'architecture

Previous6) Définir des propriétésNext8) Use Cases

Last updated 1 year ago

Was this helpful?

Avec toutes les découvertes réalisées jusqu'à présent on a pu se rendre compte que l'architecture désirée était une architecture en Onion :

On va s'assurer que le code actuel respecte le Design escompté :

  • Prendre du temps pour comprendre ce que sont des

  • Ecrire des tests d'architecture en utilisant la librairie

Pour aller plus vite, voici une classe contenant des extensions facilitant l'écriture et le lancement de tels tests :

using ArchUnitNET.Fluent;
using ArchUnitNET.Fluent.Syntax.Elements.Types;
using ArchUnitNET.Loader;
using ArchUnitNET.xUnit;
using Bouchonnois.Service;
using static ArchUnitNET.Fluent.ArchRuleDefinition;

namespace Bouchonnois.Tests.Architecture
{
    public static class ArchUnitExtensions
    {
        private static readonly ArchUnitNET.Domain.Architecture Architecture =
            new ArchLoader()
                .LoadAssemblies(typeof(PartieDeChasseService).Assembly)
                .Build();

        public static GivenTypesConjunction TypesInAssembly() =>
            Types().That().Are(Architecture.Types);

        public static void Check(this IArchRule rule) => rule.Check(Architecture);
    }
    
    // Exemple de test
    public class Guidelines
    {
        private static GivenMethodMembersThat Methods() => MethodMembers().That().AreNoConstructors().And();

        [Fact]
        public void NoGetMethodShouldReturnVoid() =>
            Methods()
                .HaveName("Get[A-Z].*", useRegularExpressions: true).Should()
                .NotHaveReturnType(typeof(void))
                .Check();
    }
}

Inward Dependencies

  • On va valider le sens des dépendances en ajoutant une nouvelle classe de tests

public class ArchitectureRules
{
    [Fact]
    public void ApplicationServicesRules() =>
    {
        // Les classes dans l'Application Services ne devraient pas dépendre de classes dans Infrastructure   
    }
        
    [Fact]
    public void InfrastructureRules() 
    {
        // Quelles sont les classes de l'infrastructure ?
        // Que devrions nous faire de ce qui est contenu dans Infra ?
    }

    [Fact]
    public void DomainModelRules() 
    {
        // Les classes dans Domain ne devraient pas dépendre de classes dans Infrastructure ou Application Services
    }
}
  • On définit les couches de notre onion :

private static GivenTypesConjunctionWithDescription ApplicationServices() =>
    TypesInAssembly().And()
        .ResideInNamespace("Service", true)
        .As("Application Services");

private static GivenTypesConjunctionWithDescription DomainModel() =>
    TypesInAssembly().And()
        .ResideInNamespace("Domain", true)
        .As("Domain Model");

private static GivenTypesConjunctionWithDescription Infrastructure() =>
    TypesInAssembly().And()
        .ResideInNamespace("Repository", true)
        .As("Infrastructure");

Domain Model

On peut alors écrire une première règle pour notre Domain Model :

[Fact]
public void DomainModelRules() =>
    DomainModel().Should()
        .NotDependOnAny(ApplicationServices()).AndShould()
        .NotDependOnAny(Infrastructure())
        .Check();

Celle-ci échoue :

La classe Terrain se trouve dans l'Application Services alors qu'elle est une entité à part entière du Domain...

On corrige cela en déplaçant la classe :

Règle de l'Application Services

On en profite pour implémenter une règle sur l'Application Service :

[Fact]
public void ApplicationServicesRules() =>
    Infrastructure().Should()
        .NotDependOnAny(Infrastructure())
        .Check();

Quid de l'infrastructure ?

Pour le moment nous n'avons qu'une interface de Repository (Un Port) au sein du namespace Infrastructure.

Est-ce que cela fait du sens au regard de la règle de dépendance ?

Nous allons déplacer ce port dans le Domain.

Nous pouvons tout de même implémenter une règle spécifiant que les items présents dans le namespace Repository doit implémenter l'interface IPartieDeChasseRepository :

[Fact]
public void InfrastructureRules() =>
    Infrastructure().Should()
        .ImplementInterface(typeof(IPartieDeChasseRepository))
        .Check();

Règles d'équipe

On peut ajouter certaines règles d'équipe du genre :

  • Toutes les interfaces doivent commencer par I

  • Une méthode commençant par Get doit retourner quelque chose

  • ...

 public class Guidelines
{
    private static GivenMethodMembersThat Methods() => MethodMembers().That().AreNoConstructors().And();

    [Fact]
    public void NoGetMethodShouldReturnVoid() =>
        Methods()
            .HaveName("Get[A-Z].*", useRegularExpressions: true).Should()
            .NotHaveReturnType(typeof(void))
            .Check();

    [Fact]
    public void IserAndHaserShouldReturnBooleans() =>
        Methods()
            .HaveName("Is[A-Z].*", useRegularExpressions: true).Or()
            .HaveName("Has[A-Z].*", useRegularExpressions: true).Should()
            .HaveReturnType(typeof(bool))
            .Check();

    [Fact]
    public void SettersShouldNotReturnSomething() =>
        Methods()
            .HaveName("Set[A-Z].*", useRegularExpressions: true).Should()
            .HaveReturnType(typeof(void))
            .Check();

    [Fact]
    public void InterfacesShouldStartWithI() =>
        Interfaces().Should()
            .HaveName("^I[A-Z].*", useRegularExpressions: true)
            .Because("C# convention...")
            .Check();
}

Reflect

  • A quoi cette technique pourrait vous servir ?

  • Quelles règles pourraient être utiles dans votre quotidien ?

Nouveau rapport SonarCloud disponible .

ici
Architecture Unit Tests
ArchUnit
Onion Architecture
Step 7 : Tests d'architecture
Failing Arch Test
Move class