Create a test assembly for architectural tests. Set a reference to DependencyAnalysis.dll. Architectural tests should run against uninstrumented code compiled with optimizations - run in Release mode rather than Debug. Debug compilation results in a lot more instructions, not only NOP instructions. If your tests pass in Debug mode they will also in Release mode, but not the other way around. Remember, you are analyzing the compiled intermediate code and not your VB, C#, F# aso source code.

Setup your Test Suite

Once per test suite let DependencyAnalysis generate the dependency model. Do not perform that task once per test case as it will take some resources to analyze your project. You can do it for example in the class initializer of a base class for your architectural tests say for example ArchitectureTest. Derive from ArchitectureTest to access the dependency model. If you feel uncomfortable in using a type initializer, use the setup method for the test suite of your unit test framework, [SetupFixture] in NUnit and [AssemblyInitialize] in MSTest. Make the model available to all your tests then.

public class ArchitectureTest { protected static readonly Setup Setup; static ArchitectureTest() { Setup = Setup.BuildStandardSetup(); Setup.StartAnalysis(new[] { "T:\\project\\Assembly1.dll", "T:\\project\\Assembly2.dll"}); } }

Rather than listing the pathes to the compiled libraries, you might also call StartAnalysis with enumeration of your projects assemblies. For example, use the typeof(SomeTypeInTheAssembly).Assembly statement to identify your modules. Personally, I add a public interface IAssemblyIdentifier to the properties folder of my solutions project. With it, I can always be sure to reference a type in my assembly that will never be moved or deleted due to refactoring.

      public class NamespaceHierarchyTest : ArchitectureTest
          public void SomeFunkyStuff()
              Setup.Verifier.VerifyThat<SubmoduleDescriptor>(x=> ...);

The Concept of Descriptors

Descriptors are representations for each architectural unit of your project. Assemblies are described by ModuleDescriptor, namespaces correspond to SubmoduleDescriptor, means that equal namespaces in two different modules are two different submodules and do not collapse to one. Types and members are represented by TypeDescriptor respectively MemberDescriptor. Any two descriptors of the same kind with the same identifier are treated as equals. So if you create a new TypeDescriptor for a type via the DescriptorBuilder, you can query the Verifier class with it. Although it is not the same instance of TypeDescriptor as stored in the dependency model. For most verifications there should be no need to create descriptors yourself.

Access to the dependency model is always indexed by IDescriotor.Identifier property.

In contrast to the representation in Intermediate language I deliberately choose to assign members of anonymous types to the types that declare the anonymous type. For example you create a LINQ statement within class MyClass. In ILDASM you would see two classes MyClass and MyClass+<>c__DisplayClassa or something similar. From an architectural point of view this is nonsense. As you define anonymous types in the context of a concrete class all their members should count for MyClass and not for an extracted type DisplayClass. Otherwise you would be enabled to cheat metrics and reduce your types complexity by putting everything into anonymous types. Moreover dependency analysis for a functional programming language like F# would be pretty useless, where anonymity is the default when you look at IL.

Egg and Gherkin is a development tool written in C# to qualify the evolvement of your software architecture within predefined limits. Controlled by the unit test framework of your choice, it gives immediate feedback when breaking architectural constraints.


Last edited Mar 9, 2014 at 5:51 PM by The_eg, version 18


No comments yet.