#region License
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using System;
using System.Reflection;
using NUnit.Framework;
using Spring.Objects.Factory.Support;
#endregion
namespace Spring.Objects.Factory{
/// <summary>
/// Unit tests for the method replacement functionality of the Spring IoC container.
/// </summary>
/// <remarks>
/// <p>
/// This encapsulates both generic method replacement and its specialization, the
/// method lookup variant.
/// </p>
/// </remarks>
/// <author>Rick Evans</author>
[TestFixture]
public sealed class MethodReplacerTests
{
[Test]
public void SunnyDayReplaceMethod()
{
RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory));
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ReturnsNullNewsFeedManager));
managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("replacer", replacerDef);
INewsFeedManager manager = (INewsFeedManager) factory["manager"];
NewsFeed feed1 = manager.CreateNewsFeed();
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
Assert.AreEqual(NewsFeedFactory.DefaultName, feed1.Name);
NewsFeed feed2 = manager.CreateNewsFeed();
// NewsFeedFactory always yields a new NewsFeed (see class definition below)...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
}
[Test]
public void SunnyDayReplaceMethod_WithProtectedVirtual()
{
RootObjectDefinition replacerDef = new RootObjectDefinition(typeof(NewsFeedFactory));
RootObjectDefinition managerDef = new RootObjectDefinition(typeof(ProtectedReturnsNullNewsFeedManagerWithVirtualMethod));
managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("replacer", replacerDef);
ProtectedReturnsNullNewsFeedManagerWithVirtualMethod manager = (ProtectedReturnsNullNewsFeedManagerWithVirtualMethod)factory["manager"];
NewsFeed feed1 = manager.GrabNewsFeed();
Assert.IsNotNull(feed1, "The protected CreateNewsFeed() method is not being replaced.");
Assert.AreEqual(NewsFeedFactory.DefaultName, feed1.Name);
NewsFeed feed2 = manager.GrabNewsFeed();
// NewsFeedFactory always yields a new NewsFeed (see class definition below)...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
}
[Test]
public void SunnyDayReplaceMethod_WithArgumentAcceptingReplacer()
{
RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory));
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerWith_Replace_MethodThatTakesArguments));
ReplacedMethodOverride theOverride = new ReplacedMethodOverride("CreateNewsFeed", "replacer");
// we must specify parameter type fragments...
theOverride.AddTypeIdentifier(typeof(string).FullName);
managerDef.MethodOverrides.Add(theOverride);
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("replacer", replacerDef);
NewsFeedManagerWith_Replace_MethodThatTakesArguments manager = (NewsFeedManagerWith_Replace_MethodThatTakesArguments) factory["manager"];
NewsFeed feed1 = manager.CreateNewsFeed("So sad... to be all alone in the world");
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
Assert.AreEqual("So sad... to be all alone in the world", feed1.Name);
NewsFeed feed2 = manager.CreateNewsFeed("Oh Muzzy!");
// NewsFeedFactory always yields a new NewsFeed (see class definition below)...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
}
[Test]
public void SunnyDayReplaceMethod_WithArgumentAcceptingReplacerWithNoOverloadingAndNoTypeFragmentsSpecified()
{
RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory));
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerWith_Replace_MethodThatTakesArguments));
ReplacedMethodOverride theOverride = new ReplacedMethodOverride("CreateNewsFeed", "replacer");
// no need to specify parameter type fragments if we turn IsOverloaded off (the method is not overloaded)...
theOverride.IsOverloaded = false;
managerDef.MethodOverrides.Add(theOverride);
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("replacer", replacerDef);
NewsFeedManagerWith_Replace_MethodThatTakesArguments manager = (NewsFeedManagerWith_Replace_MethodThatTakesArguments) factory["manager"];
NewsFeed feed1 = manager.CreateNewsFeed("So sad... to be all alone in the world");
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
Assert.AreEqual("So sad... to be all alone in the world", feed1.Name);
NewsFeed feed2 = manager.CreateNewsFeed("Oh Muzzy!");
// NewsFeedFactory always yields a new NewsFeed (see class definition below)...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
}
[Test]
public void SunnyDayReplaceMethod_WithReplacerThatReturnsVoid()
{
RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (DoNothingReplacer));
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerThatReturnsVoid));
ReplacedMethodOverride theOverride = new ReplacedMethodOverride("DoSomething", "replacer");
managerDef.MethodOverrides.Add(theOverride);
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("replacer", replacerDef);
NewsFeedManagerThatReturnsVoid manager = (NewsFeedManagerThatReturnsVoid) factory["manager"];
manager.DoSomething();
}
/// <summary>
/// We don't specify any type fragments, so the method overload check will never pass.
/// </summary>
[Test]
[ExpectedException(typeof(NotImplementedException))]
public void SunnyDayReplaceMethod_WithArgumentAcceptingReplacerWithNoTypeFragmentsSpecified()
{
RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory));
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerWith_Replace_MethodThatTakesArguments));
managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("replacer", replacerDef);
NewsFeedManagerWith_Replace_MethodThatTakesArguments manager = (NewsFeedManagerWith_Replace_MethodThatTakesArguments) factory["manager"];
manager.CreateNewsFeed("So sad... to be all alone in the world");
}
/// <summary>
/// A class that requires two lookup method injections.
/// </summary>
[Test]
public void LookupMethodMultiple()
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition testObjectDef = new RootObjectDefinition(typeof (TestObject));
testObjectDef.IsSingleton = false;
testObjectDef.PropertyValues.Add("name", "Miki Nakatani");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (TestObjectAndNewsFeedFactory));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateTestObject", "test"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
factory.RegisterObjectDefinition("test", testObjectDef);
TestObjectAndNewsFeedFactory manager = (TestObjectAndNewsFeedFactory) factory["manager"];
INewsFeedManager newsFeedManager = manager;
NewsFeed feed1 = newsFeedManager.CreateNewsFeed();
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
NewsFeed feed2 = newsFeedManager.CreateNewsFeed();
// assert that the object (prototype) is definitely being looked up each time...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
ITestObjectFactory toFactory = manager;
ITestObject to1 = toFactory.CreateTestObject();
Assert.IsNotNull(to1, "The CreateTestObject() method is not being replaced.");
ITestObject to2 = toFactory.CreateTestObject();
// assert that the object (prototype) is definitely being looked up each time...
Assert.IsFalse(ReferenceEquals(to1, to2));
}
/// <summary>
/// A class that requires both lookup method and replace method injection.
/// </summary>
[Test]
public void LookupAndReplaceMethod()
{
RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory));
RootObjectDefinition testObjectDef = new RootObjectDefinition(typeof (TestObject));
testObjectDef.IsSingleton = false;
testObjectDef.PropertyValues.Add("name", "Miki Nakatani");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (TestObjectAndNewsFeedFactory));
managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer"));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateTestObject", "test"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("replacer", replacerDef);
factory.RegisterObjectDefinition("test", testObjectDef);
TestObjectAndNewsFeedFactory manager = (TestObjectAndNewsFeedFactory) factory["manager"];
INewsFeedManager newsFeedManager = manager;
NewsFeed feed1 = newsFeedManager.CreateNewsFeed();
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
NewsFeed feed2 = newsFeedManager.CreateNewsFeed();
// assert that the object (prototype) is definitely being looked up each time...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
ITestObjectFactory toFactory = manager;
ITestObject to1 = toFactory.CreateTestObject();
Assert.IsNotNull(to1, "The CreateTestObject() method is not being replaced.");
ITestObject to2 = toFactory.CreateTestObject();
// assert that the object (prototype) is definitely being looked up each time...
Assert.IsFalse(ReferenceEquals(to1, to2));
}
/// <summary>
/// Lookup method injection on an (interface) method that returns null.
/// </summary>
[Test]
public void LookupMethodWithNullMethod()
{
try
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ReturnsNullNewsFeedManager));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
INewsFeedManager manager = (INewsFeedManager) factory["manager"];
NewsFeed feed1 = manager.CreateNewsFeed();
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
NewsFeed feed2 = manager.CreateNewsFeed();
// assert that the object (prototype) is definitely being looked up each time...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
}
catch (Exception ex)
{
Console.Out.WriteLine("ex = {0}", ex);
}
}
/// <summary>
/// Lookup method injection on an (interface) method that returns null.
/// </summary>
/// <remarks>
/// <p>
/// The only difference from the previous (similarly named) test is that
/// constructor arguments are passed to the (er) constructor as it (the
/// dynamic subclass) is being instantiated by the container.
/// </p>
/// </remarks>
[Test]
public void LookupMethodWithNullMethodInstantiatedWithCtorArg()
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ReturnsNullNewsFeedManager));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
managerDef.ConstructorArgumentValues.AddNamedArgumentValue("name", "Bingo");
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
ReturnsNullNewsFeedManager manager = (ReturnsNullNewsFeedManager) factory["manager"];
Assert.AreEqual("Bingo", manager.Name);
NewsFeed feed1 = manager.CreateNewsFeed();
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
NewsFeed feed2 = manager.CreateNewsFeed();
// assert that the object (prototype) is definitely being looked up each time...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
}
[Test]
public void LookupMethodWithAbstractMethod()
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (AbstractNewsFeedManager));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
INewsFeedManager manager = (INewsFeedManager) factory["manager"];
NewsFeed feed1 = manager.CreateNewsFeed();
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
NewsFeed feed2 = manager.CreateNewsFeed();
// assert that the object (prototype) is definitely being looked up each time...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
}
/// <summary>
/// Protected methods can also be method injected.
/// </summary>
[Test]
public void LookupMethodWithVirtualProtectedMethod()
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ProtectedReturnsNullNewsFeedManagerWithVirtualMethod));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
ProtectedReturnsNullNewsFeedManagerWithVirtualMethod manager = (ProtectedReturnsNullNewsFeedManagerWithVirtualMethod) factory["manager"];
NewsFeed feed1 = manager.GrabNewsFeed();
Assert.IsNotNull(feed1, "The CreateNewsFeed() method is not being replaced.");
NewsFeed feed2 = manager.GrabNewsFeed();
// assert that the object (prototype) is definitely being looked up each time...
Assert.IsFalse(ReferenceEquals(feed1, feed2));
}
#region Rainy Day Scenarios
[Test]
[ExpectedException(typeof (ObjectCreationException))]
public void FailWithLookupMethodOnSealedClass()
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (SealedReturnsNullNewsFeedManager));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
factory.GetObject("manager");
}
[Test]
[ExpectedException(typeof (ObjectCreationException))]
public void FailWithReplaceMethodOnSealedClass()
{
RootObjectDefinition replacerDef = new RootObjectDefinition(typeof (NewsFeedFactory));
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (SealedReturnsNullNewsFeedManager));
managerDef.MethodOverrides.Add(new ReplacedMethodOverride("CreateNewsFeed", "replacer"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("replacer", replacerDef);
factory.GetObject("manager");
}
/// <summary>
/// Tests that one cannot (obviously) override a protected method that has
/// not been explicitly declared virtual on the original class declaration.
/// </summary>
[Test]
[ExpectedException(typeof (ObjectCreationException))]
public void FailOnNonVirtualProtectedMethod()
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ProtectedReturnsNullNewsFeedManager));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
factory.GetObject("manager");
}
/// <summary>
/// Tests that one cannot (obviously) method inject a method with a void
/// return type.
/// </summary>
[Test]
[ExpectedException(typeof (ObjectCreationException))]
public void FailOnMethodWithVoidReturnType()
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (ReturnsVoidNewsFeedManager));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
factory.GetObject("manager");
}
/// <summary>
/// Lookup methods cannot accept any arguments (replace methods can though).
/// </summary>
[Test]
[ExpectedException(typeof (ObjectCreationException))]
public void FailOnLookupMethodThatHasArguments()
{
RootObjectDefinition feedDef = new RootObjectDefinition(typeof (NewsFeed));
feedDef.IsSingleton = false;
feedDef.PropertyValues.Add("name", "Bingo");
RootObjectDefinition managerDef = new RootObjectDefinition(typeof (NewsFeedManagerWithLookupMethodThatTakesArguments));
managerDef.MethodOverrides.Add(new LookupMethodOverride("CreateNewsFeed", "feed"));
DefaultListableObjectFactory factory = new DefaultListableObjectFactory();
factory.RegisterObjectDefinition("manager", managerDef);
factory.RegisterObjectDefinition("feed", feedDef);
factory.GetObject("manager");
}
#endregion
}
public interface INewsFeedManager
{
NewsFeed CreateNewsFeed();
}
public class ReturnsNullNewsFeedManager : INewsFeedManager
{
private string name;
public ReturnsNullNewsFeedManager()
{
}
public ReturnsNullNewsFeedManager(string name)
{
this.name = name;
}
public virtual NewsFeed CreateNewsFeed()
{
return null;
}
public string Name
{
get { return name; }
}
}
public sealed class SealedReturnsNullNewsFeedManager : INewsFeedManager
{
public NewsFeed CreateNewsFeed()
{
return null;
}
}
public class ProtectedReturnsNullNewsFeedManager
{
public NewsFeed GrabNewsFeed()
{
return CreateNewsFeed();
}
protected NewsFeed CreateNewsFeed()
{
return null;
}
}
public class ProtectedReturnsNullNewsFeedManagerWithVirtualMethod
{
public NewsFeed GrabNewsFeed()
{
return CreateNewsFeed();
}
protected virtual NewsFeed CreateNewsFeed()
{
return null;
}
}
/// <summary>
/// Just why anyone would want to do method injection on a method with a
/// void return type is a question best left for another (earlier) night.
/// </summary>
public class ReturnsVoidNewsFeedManager
{
public virtual void CreateNewsFeed()
{
}
}
public class NewsFeedManagerWithLookupMethodThatTakesArguments
{
public virtual NewsFeed CreateNewsFeed(string foo, int bar)
{
return null;
}
}
public class NewsFeedManagerThatReturnsVoid
{
public virtual void DoSomething()
{
}
}
public class DoNothingReplacer : IMethodReplacer
{
public object Implement(object target, MethodInfo method, params object[] arguments)
{
return null;
}
}
public class NewsFeedManagerWith_Replace_MethodThatTakesArguments
{
public virtual NewsFeed CreateNewsFeed(string headline)
{
throw new NotImplementedException();
}
}
public abstract class AbstractNewsFeedManager : INewsFeedManager
{
public abstract NewsFeed CreateNewsFeed();
}
public sealed class NewsFeedFactory : IMethodReplacer
{
public const string DefaultName = "All The News That's Fit To Print";
public object Implement(object target, MethodInfo method, params object[] arguments)
{
if (arguments != null && arguments.Length > 0)
{
string headline = (string) arguments[0];
return new NewsFeed(headline);
}
return new NewsFeed(DefaultName);
}
}
public sealed class NewsFeed
{
private string name;
public NewsFeed()
{
}
public NewsFeed(string name)
{
this.name = name;
}
public string Name
{
get { return name; }
set { name = value; }
}
}
public interface ITestObjectFactory
{
ITestObject CreateTestObject();
}
public class TestObjectAndNewsFeedFactory : ITestObjectFactory, INewsFeedManager
{
public virtual ITestObject CreateTestObject()
{
throw new NotImplementedException();
}
public virtual NewsFeed CreateNewsFeed()
{
throw new NotImplementedException();
}
}
}
|