Buddy Injector was designed to simplify unit testing processes that involve numerous injections.
Buddy Injector intends to be as simple as possible. It does not intend to be a robust dependency injector, to be used in large scale, or as your main dependency container.
dotnet add package diegodrf.BuddyInjector
The BuddyInjector
class is the main point to interact with the dependencies. Here you will be able to register and get the dependencies.
It's possible and recommended to call Dispose
after using the container. Disposing BuddyInjector
will call Dispose for all dependencies that implemented it.
BuddyInjector buddyInjector = new BuddyInjector();
// ...
buddyInjector.Dispose();
You can use the using
statement.
using (BuddyInjector buddyInjector = new BuddyInjector())
{
// ...
}
BuddyInjector allows you to register two life cycles, Transient and Singleton. Boot accepts two ways, implicit and explicit.
RegisterSingleton<TType, TImplementation>()
RegisterSingleton<T>(Func<T> instanceBuilder)
using (BuddyInjector buddyInjector = new BuddyInjector())
{
buddyInjector.RegisterSingleton<IFoo, Foo>();
buddyInjector.RegisterSingleton<IFoo>(() => new Foo());
}
interface IFoo { }
class Foo : IFoo { }
RegisterTransient<TType, TImplementation>()
RegisterTransient<T>(Func<T> instanceBuilder)
using (BuddyInjector buddyInjector = new BuddyInjector())
{
buddyInjector.RegisterTransient<IFoo, Foo>();
buddyInjector.RegisterTransient<IFoo>(() => new Foo());
}
interface IFoo { }
class Foo : IFoo { }
When we create tests it's normal to instantiate the same class a lot of times to cover all the tests. So to help with this boring task BuddyInjector has the RegisterAll
.
The RegisterAll
method expects an Action
that receives an instance of BuddyInjector
. So you can create a base implementation to instantiate all your classes, like the real implementations for example, and substitute them per test with the specific mocks.
using diegodrf.BuddyInjector;
Action<BuddyInjector> instantiateAll = buddyInjector =>
{
buddyInjector.RegisterSingleton<IFoo, Foo>();
buddyInjector.RegisterSingleton<IBar, Bar>();
};
using (BuddyInjector buddyInjector = new BuddyInjector())
{
buddyInjector.RegisterAll(instantiateAll);
buddyInjector.GetInstance<IBar>();
}
interface IFoo { }
interface IBar { }
class Foo : IFoo { }
class Bar(IFoo foo) : IBar { }
The behavior of Registers is to override registered instances. It allows you to register the real implementations and for each test that you need a mock you can replace it with it.
using (BuddyInjector buddyInjector = new BuddyInjector())
{
// Register real implementation
buddyInjector.RegisterSingleton<IFoo, RealImplementation>();
// Register mocked implementation
buddyInjector.RegisterSingleton<IFoo, MockImplementation>();
// Get IFoo
IFoo foo = buddyInjector.GetInstance<IFoo>();
Console.WriteLine(foo is RealImplementation); // False
Console.WriteLine(foo is MockImplementation); // True
}
interface IFoo { }
class RealImplementation : IFoo { }
class MockImplementation : IFoo { }
To use the implicit registration BuddyInjector expects that the class has only one constructor. If there is more than one, it will throw MultipleConstructorsException
.
When the class has more than one constructor, you should register using the explicit constructor.
using (BuddyInjector buddyInjector = new BuddyInjector())
{
buddyInjector.RegisterSingleton<IFoo>(() => new Foo(true));
}
interface IFoo { }
class Foo : IFoo
{
// First constructor
public Foo() { }
// Second constructor
public Foo(bool someValue) { }
}
If you try to register a not concrete class, like interface or abstract class, BuddyInjector will fail fast trowing ArgumentException
.
Example:
using (BuddyInjector buddyInjector = new BuddyInjector())
{
buddyInjector.RegisterSingleton<IFoo, IFoo>(); // It throws [ArgumentException].
}
interface IFoo { }
class Foo : IFoo { }
To retrieve the instances you use RegisterTransient GetInstance<T>()
.
using (BuddyInjector buddyInjector = new BuddyInjector())
{
buddyInjector.RegisterTransient<IFoo, Foo>();
IFoo foo = buddyInjector.GetInstance<IFoo>();
}
interface IFoo { }
class Foo : IFoo { }
If you try to retrieve an object not registered, you will receive the exception NotRegisteredException
.