Test your dependency setup

Zdarzało wam się skończyć kodzić feature w aplikacji webowej, z wszystkimi testami, clean code i w ogóle..
A jednak przy pierwszym odpaleniu BANG! Cała aplikacja zdycha?

Na pewno większość developerów zderzyła się z podobnym wyjątkiem:

“No matching bindings are available, and the type is not self-bindable.” (Ninject)

Dependency Injection Container

Oczywiście winą jest źle skonfigurowany kontener zależności (Autofac, Ninject czy inny StructureMap).
Nie jest to jakiś duży problem, wystarczy poprawić kilka bindingów i po kłopocie.

Ale niesmak pozostaje, zawsze trzeba pamiętać aby ten binding dodać/poprawić przy zmianie kodu.
A jak wiemy programiści lubią zapominać…

Testowanie kontenera

Warto napisać sobie jeden mały test który sprawdza czy wszystkie controllery mogą być poprawnie utworzone przez kontener:

        [Test]
        public void AllDependenciesAreCorrectlyWired()
        {
            HttpContext.Current = new HttpContext(
                new HttpRequest("", "http://tempuri.org", ""),
                new HttpResponse(new StringWriter()));
            
            var kernel = IoC.CreateKernel();

            var mvcAssembly = typeof(IoC).Assembly;

            var controllerTypes = mvcAssembly.GetExportedTypes()
                .Where(type => typeof(IHttpController).IsAssignableFrom(type))
                .Where(type => !type.IsAbstract)
                .Where(type => !type.IsGenericTypeDefinition)
                .ToArray();
            
            foreach (var controllerType in controllerTypes)
            {
                var constructors = controllerType.GetConstructors().Where(x => x.IsPublic).ToList();
                Check.That(constructors).HasSize(1);
                Check.ThatCode(() => constructors.Single()
                    .GetParameters()
                    .Select(x => x.ParameterType)
                    .Select(x => kernel.Get(x))
                    .ToList())
                    .DoesNotThrow();
            }
        }

Jeden niuans: jak znaleźć assembly z wszystkimi controllerami?
Ja akurat używam klasy w której trzymam bindingi Ninjecta – IoC.

Szczególnie ważne jest to gdy współdzielimy kod między aplikacją webową a dowolną inną.

Zmiany współdzieloncyh klas mogą wykrzaczyć kontener webowy.
I najprawdopodobniej, nie zostanie on sprawdzony przy implementowaniu funkcjonalności do aplikacji desktopowej.

Be First to Comment

A penny for your thoughts