Convention tests – reflection

One of the first thing that I’ve found learning C# is how powerful reflection is here. It is also easy to use it in comparison to Java. The code looks often more elegant and clean. Today I would like to take a look at reflection api in order to found parts that might be useful for convention testing.

Getting types from assembly

There are two basic ways of getting assemblies – one is to go through AppDomain:

AppDomain.CurrentDomain.GetAssemblies()

Second is using type object:

typeof(Foo).Assembly

In the documentation of GetAssemblies method you can find that this method will return all assemblies that are loaded into execution context. But you cannot be sure if assembly that you want to scan is loaded. At the other hand – problem with getting assembly from Type instance is… you need to have Type instance.

When you have your Assembly instance – 99% the times you would like to call GetTypes method that will return you (surprise, surprise…) all types in given assembly. But there is second method that you might want to consider – GetExportedTypes. When you are providing assembly that will be used in other project it might be important to you to strictly define which types should be internal and which should be exposed outside assembly. The GetExportedTypes allows you to check the subset of all types from assembly that you want to publish.

Finding types that you want to test

The fact that we have all types from assembly does not help us much. We need a way to filter this set in order to get only the types that we want to test later. For example we might want only types to have specific names, or from specific namespace. Here are couple of methods that will be useful for us for filtering.

Filtering by name:

  • Type.Namespace – contains name of namespace for given type
  • Type.Name – contains name of type (duh!)

Filtering by type of “thing”:

  • Type.IsInterface – check if given type is an interface
  • Type.IsClass – check if given type is a class
  • Type.IsEnum – check if given type is an enum
  • Type.IsAbstract – check if given type is abstract – so either abstract class or interface
  • Type.IsValueType – check if given type is value type – so either primitive, struct or enum

Filtering by inheritance relationship:

  • Type.IsSubclassOf(t) – allows to check if our type is subclass of t – this will not work with interfaces, and this method will not return true for type t itself
  • Type.IsAssignableFrom(t) – allows to check if to reference of type t we can assign current type. So basically type either implements t or is subclass of t

We can of course mix our filters. In some cases even we have to – for example to get all structures we should check if type is value type but at the same type is not enum nor primitive type.

But how exactly we should filter things? I like to wrap everything inside Linq expression. So for example – to get list of all interfaces in foo.bar.baz namespace we can do something like this:

var interfacesList = typeof(Customer).Assembly
    .GetTypes()
    .Where(t => t.Namespace.Equals("foo.bar.baz") && t.IsInterface)
    .ToList();

If you want to make it more redable you might create helper method and get something like this

var interfacesList = typeof(Customer).Assembly
    .GetTypes()
    .Where(InterfaceInNamespace(“foo.bar.baz”))
    .ToList();

Testing our types

When we had found types that we want to test – there is a time for an acctual testing. And we can do quite a lot:

  • most simple scenario is to call for Type.Name attribute to check if name of our type follows some convention (for example ends with some suffix)
  • in similar way when calling for Type.Namespace we assert if our type is part of some exact namespace
  • with Type.BaseType we are getting super type. This property returns another Type instance so we can test it for whatever is inside – the most obvious might be to check if base type is of some exact type we want.
  • with Type.GetInterfaces we are getting array of interfaces that given type is implementing. Again this method returns array of Type instances – in most common scenario we might just check if this array contains some exact interface.
  • Type.GetCustomAttributes returns array of attributes that are assigned to our type. This type result is of type of object, so if we want to seek for some exact attribute we need to call GetType() on each element of array
  • If our type is enum then we can use for example Type.GetEnumNames() to assert enum constants.

Ok, and how to check? Again – with Linq – let’s take our previous example (all interfaces in “foo.bar.baz”). We will add to it checking if name of the interfaces follows default c# naming convention – so we will check if name starts with capital “I” letter.

var interfacesList = typeof(Customer).Assembly
    .GetTypes()
    .Where(t => t.Namespace.Equals("foo.bar.baz") && t.IsInterface)
    .Where(t => t.Name.StartsWith("I"))
    .ToList();

But wait… it is not to useful to check for all types that matches convention. It is much more useful to check for all types that does NOT match convention – if there is any – we should fail the test:

var count = typeof(Customer).Assembly
    .GetTypes()
    .Where(t => t.Namespace.Equals("foo.bar.baz") && t.IsInterface)
    .Where(t => !t.Name.StartsWith("I"))
    .Count();
Assert.AreEqual(0, count);

At this point test is useful – will fail for each type that does not follows convention, but error message will not be to useful for us. Let’s change this code a little:

typeof(Customer).Assembly
    .GetTypes()
    .Where(t => t.Namespace.Equals("foo.bar.baz") && t.IsInterface)
    .Where(t => t.Name.StartsWith("I"))
    .ToList()
    .ForEach(t => Assert.Fail(string.Format("type {0} does not starts with capital letter 'I'", t.FullName)

Now we are good. We can introduce some more polishing if we want – but it should depends on our (or team’s) definition of readability. We might also extract some parts to separate methods for either more readability or for reuse in other test cases.

In previous section I’ve shown you some building blocks for finding types we want. In this section I’ve shown you some building blocks for testing types. But we can totally mix them. For example we can find all classes from given namespace and test if all of them are enums.

Finding methods

Types are not only “things” that we can test – we can extract from types everything that can be “called” – so methods, constructors, properties or events. Type class contains following helpful methods:

  • Type.GetMethods() returns MethodInfo
  • Type.GetProperties() returns PropertyInfo
  • Type.GetEvents() returns EventInfo
  • Type.GetConstructors() returns ConstructorInfo

Methods names should be self-explainatory. Returned types are different but there are quite similar api inside of them. Usually we might want to mix above methods with some other predicates – for example we might want to get all public methods. And if we speak about properties there are two more useful methods:

  • PropertyInfo.GetGetMethod() – returns accessor method of property
  • PropertyInfo.GetSetMethod() – returns mutator method of property

Testing methods

Here are some useful methods for testing… well.. methods  (I’m focusing on MethodInfo type – since it will be most often used):

  • MethodInfo.Name – for testing method name
  • MethodInfo.GetPrameters() – for getting list of parameters. This might be useful with ConstructorInfo type – for example if we want to check if class have parameterless constructor.
  • MethodInfo.IsPublic allows us to check if method is public (there is of course IsPrivate, but more often we would like to check convention for public methods)
  • MethodInfo.IsAbstract – allows to check if method is abstract (so either method with abstract keyword or any method from interface)
  • MethodInfo.IsVirtual – allows to check if method is virtual

Time for quick example. Let’s modify previous one. Right now I want to get all interface from “foo.bar.baz” namespace and assert that each method name contains “Bar” substring:

typeof(Customer).Assembly
    .GetTypes()
    .Where(t => t.Namespace.Equals("foo.bar.baz") && t.IsInterface)
    .SelectMany(t => t.GetMethods())
    .Where(m => !m.Name.StartsWith("Get") && !m.Name.Equals("ToString") && !m.Name.Equals("Equals" )
    .ToList()
    .ForEach(m => Assert.Fail(string.Format("method {0} of type {1} does not contains 'Bar' in name", m.Name, m.DeclaringType.Name);

It is worth to notice that extraction of methods is performed with SelectMany method – becasue of that I can work with MethodInfo type in next step instead of MethodInfo[] array. Important thing to remember – you should be aware about unwanted inherited methods that you should filter out. For example Equals and ToString.

Summary

Reflection gives us possibility to check many things about our code – it is only matter of checking what reflection methods and types are available for us and think how to use them to model our convention test.

Code for the whole series would be available here: https://github.com/mprzybylak/CSharpConventionTests

Links

AppDomain.GetAssemblies Method ()
Assembly.GetTypes Method ()
Assembly.GetExportedTypes Method ()
Type Class
MethodInfo Class
PropertyInfo Class
ConstructorInfo Class
EventInfo Class