stiank81
stiank81

Reputation: 25686

Why can't I inject value null with Ninjects ConstructorArgument?

When using Ninjects ConstructorArgument you can specify the exact value to inject to specific parameters. Why can't this value be null, or how can I make it work? Maybe it's not something you'd like to do, but I want to use it in my unit tests.. Example:

public class Ninja
{
    private readonly IWeapon _weapon;
    public Ninja(IWeapon weapon)
    {
        _weapon = weapon;
    }
}

public void SomeFunction()
{
    var kernel = new StandardKernel();
    var ninja = kernel.Get<Ninja>(new ConstructorArgument("weapon", null));
}

Upvotes: 5

Views: 910

Answers (4)

Finglas
Finglas

Reputation: 15709

I want to use it in my unit tests

There is no need to use an IOC container for unit tests. You should use the container to wire you application together at runtime, and nothing more. And if that begins to hurt, its a smell indicating your class is getting out of hand (SRP violation?)

Your unit test would then be in this example:

var ninja = new Ninja(null);

The above is legit C# code, and passing a null reference for unit testing is a perfectly valid way of unit testing areas which don't need the dependency.

Upvotes: 3

Ruben Bartelink
Ruben Bartelink

Reputation: 61795

Looking at the source (and the stack trace I got by reproing which you omitted :P)

This is because it's binding to a different overload of the ConstructorArgument ctor than the normal usage (i.e., where you're passing a Value Type or a non-null Reference Type) does.

The workaround is to cast the null to Object:-

var ninja = kernel.Get<Ninja>( new ConstructorArgument( "weapon", (object)null ) );

Ninject 2 source:

public class ConstructorArgument : Parameter
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ConstructorArgument"/> class.
    /// </summary>
    /// <param name="name">The name of the argument to override.</param>
    /// <param name="value">The value to inject into the property.</param>
    public ConstructorArgument(string name, object value) : base(name, value, false) { }

    /// <summary>
    /// Initializes a new instance of the <see cref="ConstructorArgument"/> class.
    /// </summary>
    /// <param name="name">The name of the argument to override.</param>
    /// <param name="valueCallback">The callback to invoke to get the value that should be injected.</param>
    public ConstructorArgument(string name, Func<IContext, object> valueCallback) : base(name, valueCallback, false) { }
}

Repro:

public class ReproAndResolution
{
    public interface IWeapon
    {
    }

    public class Ninja
    {
        private readonly IWeapon _weapon;
        public Ninja( IWeapon weapon )
        {
            _weapon = weapon;
        }
    }

    [Fact]
    public void TestMethod()
    {
        var kernel = new StandardKernel();
        var ninja = kernel.Get<Ninja>( new ConstructorArgument( "weapon", (object)null ) );
    }
}

Lesson? You'd be crazy not to download the latest source and look at it. Great comments, nice clean codebase. Thanks again to @Ian Davis for that tip/prodding!

Upvotes: 7

Brian Rasmussen
Brian Rasmussen

Reputation: 116401

I don't know Ninject, but AFAIK constructor injection is commonly used for mandatory dependencies and thus null makes little sense in this context. If the dependency is not mandatory the type should provide a default constructor and use property injection instead.

This post provides additional info.

Upvotes: 3

Gerrie Schenck
Gerrie Schenck

Reputation: 22368

This is probably unsupported because constructor arguments can be value types too.

Upvotes: 0

Related Questions