1

Using Ninject, I have the following and wish to test using FluentAssertions:

[Test]
public void InterfacesEndingWithFactoryShouldBeBoundAsFactories() {
    // Given
    IKernel kernel = new StandardKernel();
    kernel.Bind(services => services
        .From(AppDomain.CurrentDomain
            .GetAssemblies()
            .Where(a => !a.FullName.Contains("Tests")))
        .SelectAllInterfaces()
        .EndingWith("Factory")
        .BindToFactory()
    );

    // When
    var factory = kernel.Get<ICustomerManagementPresenterFactory>();

    // Then
    factory.Should().NotBeNull();
}

Is there any good ways to test whether the factories are actually bound properly?

Will Marcouiller
  • 23,773
  • 22
  • 96
  • 162
  • To any SO community member, I have accepted @KevinKuszyk answer as he provides a way to do what I asked. Besides, one shall also consider BatteryBackupUnit's answer as it is also very enlightening. Both answers make big sense. – Will Marcouiller Dec 05 '14 at 14:15

2 Answers2

2

I wrote an extension package for Fluent Assertions to test my Ninject bindings. Using it your test could be rewritten like this:

[Test]
public void InterfacesEndingWithFactoryShouldBeBoundAsFactories() {
    // Given
    IKernel kernel = new StandardKernel();
    kernel.Bind(services => services
        .From(AppDomain.CurrentDomain
            .GetAssemblies()
            .Where(a => !a.FullName.Contains("Tests")))
        .SelectAllInterfaces()
        .EndingWith("Factory")
        .BindToFactory()
    );

    // When


    // Then
    kernel.Should().Resolve<ICustomerManagementPresenterFactory>().WithSingleInstance()
}

As suggested by @Will Marcouiller, I would also extract the code to bootstrap the kernel into it's own class so it can be invoked in your app's composition root and in your unit tests.

Kevin Kuszyk
  • 1,958
  • 24
  • 37
1

First off, there is no ninject infrastructure to test which types are affected and which not. Unit testing is very complicated due to the fluent syntax and it's many interfaces which are returned when calling a method. So all you can practically do is integration testing.

Now, in my opinion, the From(...) is problematic. If you'd create a component where you could replace the assemblies you pass to From , create an extra test assembly containing a few types, and then test whether lets say interface IFoo is not bound, interface IFooFactory is bound, class Foo is not bound,.. you would have a functioning integration test.

Consider however, that if there is no binding for IFooFactory and SomeClass uses IFooFactory as constructor argument, ninject will throw an exception. Now if you have a composition root simply starting up the application will tell you whether the necessary bindings exist or not.

This test is even more useful than the factory convention integration test. Consider if someone accidentally bound the factory, manually, too. This won't show up with the convention-integration test, but starting the application, ninject will throw an exception stating that there are multiple bindings for this one interface.

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • In fact, I noticed that I needed to specify a `Where(a => a.FullName.Contains("MyProject")` for this convention to work, otherwise it took all of any other types of the .NET Framework as well. And I also strive to follow Mark Seemann's guidelines, so good point on the `CompositionRoot`. I even have written a `CompositionRoot` class where I compose the object graph based on the conventions I use, and I simply call `CompositionRoot.ComposeObjectGraph()` in my program startup. – Will Marcouiller Sep 11 '14 at 11:59