If you already happen to be an expert and see any mistakes or have suggestions, I would appreciate feedback.
Download Source if you don't want to read and just want to see the code in action.
Let's take the following code:
public interface IMyObject
{
string GetValue();
}
public class ImplMyObject : IMyObject
{
public string GetValue()
{
return "ImplMyObject";
}
}
public class MyObjectHandler
{
public string GetValueFromMyObject(IMyObject myObj)
{
return myObj.GetValue();
}
}
Very simple, if we want to invoke it, we'll do the following:static void Main(string[] args)
{
ImplMyObject myObj = new ImplMyObject();
MyObjectHandler handler = new MyObjectHandler();
string myObjValue = handler.GetValueFromMyObject(myObj);
}
Nothing wrong with that, we can pass in any object inheriting from the "IMyObject" interface and get the expected result.Now, let's say we want to write a test case to test "MyObjectHandler." We could do the following:
public class MockMyObject : IMyObject
{
public string GetValue()
{
return "MockMyObject";
}
}
[TestMethod]
public void RegularSample()
{
MockMyObject myObj = new MockMyObject();
MyObjectHandler handler = new MyObjectHandler();
Assert.AreEqual("MockMyObject", handler.GetValueFromMyObject(myObj));
}
But now we're cluttering up our code with Mock objects, which really only mean something within a test case and should be maintained with the test case. So why not ignore the object and use Moq?[TestMethod]
public void MoqObjectSample()
{
var moqMyObj = new Mock<IMyObject>();
moqMyObj.Setup(m => m.GetValue()).Returns("MoqObjectSample");
MyObjectHandler handler = new MyObjectHandler();
Assert.AreEqual("MoqObjectSample", handler.GetValueFromMyObject(moqMyObj.Object));
}
So far, so good, right? We really don't need to recreate a full object, just Moq it up to return certain results which will test the "MyObjectHandler" code.Now, let's introduce Ninject into the picture. There may be times when you aren't able to pass interfaces around, or there are too many functions to pass it around if we have deep code and we can't possibly manage passing a DB connection object from our UI layer to our business layer and ultimately to the data access layer. We're still passing interfaces, but we just don't start at the top. So we can't really use Mock objects if we can't pass them in.
Let's look at the following example:
public class MyObjectHandler
{
public string GetValueFromMyObject()
{
IMyObject myObj = new ImplMyObject();
return myObj.GetValue();
}
}
Not conducive to much of anything. Let's add a few lines of Ninject code...internal class NinjectBootstrapper
{
private static readonly Lazy<IKernel> _kernel =
new Lazy<IKernel>(() => new StandardKernel(new Bindings()));
internal static IKernel Kernel
{
get { return _kernel.Value; }
}
}
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<IMyObject>().To<ImplMyObject>();
}
}
So above we created the binding for IMyObject to use ImplMyObject and we'll use it as follows:public string GetValueFromMyObject()
{
IMyObject myObj = NinjectBootstrapper.Kernel.Get<IMyObject>();
return myObj.GetValue();
}
Simple and elegant, we can now manage all of our bindings in the Bindings.cs. So why would we want to do this? One reason might be to bind to a mock object in the test code as follows:[TestMethod]
public void NinjectRebindSample()
{
NinjectBootstrapper.Kernel.Rebind<IMyObject>().To<MockMyObject>();
MyObjectHandler handler = new MyObjectHandler();
Assert.AreEqual("MockMyObject", handler.GetValueFromMyObject());
}
Let's combine the above with a Moq object....[TestMethod]
public void MoqObjectSample()
{
var moqMyObj = new Mock<IMyObject>();
moqMyObj.Setup(m => m.GetValue()).Returns("MoqObjectSample");
NinjectBootstrapper.Kernel.Rebind<IMyObject>().ToConstant(moqMyObj.Object);
MyObjectHandler handler = new MyObjectHandler();
Assert.AreEqual("MoqObjectSample", handler.GetValueFromMyObject());
}
This can get a lot more complex, but a lot more useful. It's just an example to get started. Also keep in mind that there are performance penalties for using Ninject. In most situations this won't matter (and there are techniques to optimize it).