From a32db83dee742dc7cf11a12ef2ea76c2f29b995f Mon Sep 17 00:00:00 2001 From: Gustavo Russo Date: Sat, 19 Feb 2011 10:38:30 +0100 Subject: [PATCH] Changed default return value of collection interfaces to return an instance of an empty collection when the method have no expectations. It will avoid the need to stub methods that returns collections just to avoid null reference exceptions when the method does not matter in the test --- Rhino.Mocks.Tests/StubTest.cs | 14 +++++ .../Utilities/ReturnValueUtilTests.cs | 57 ++++++++++++++++++- Rhino.Mocks/Utilities/ReturnValueUtil.cs | 39 ++++++++++++- 3 files changed, 107 insertions(+), 3 deletions(-) diff --git a/Rhino.Mocks.Tests/StubTest.cs b/Rhino.Mocks.Tests/StubTest.cs index 2f2663a9..8945fdd1 100644 --- a/Rhino.Mocks.Tests/StubTest.cs +++ b/Rhino.Mocks.Tests/StubTest.cs @@ -27,6 +27,7 @@ #endregion using System; +using System.Collections.Generic; using Xunit; using Rhino.Mocks.Interfaces; @@ -59,6 +60,17 @@ public void StubHasPropertyBehaviorForAllProperties() Assert.Equal("Caucasusian Shepherd", animal.Species); } + [Fact] + public void DefaultValue_OfAMethodThatHasNoExpectationsAndReturnACollection_ReturnAnEmptyCollection() + { + var animal = MockRepository.GenerateStub(); + + var parents = animal.GetParents(); + + Assert.NotNull(parents); + Assert.Equal(0, parents.Count); + } + [Fact] public void CanRegisterToEventsAndRaiseThem() { @@ -132,6 +144,8 @@ public interface IAnimal event EventHandler Hungry; string GetMood(); + + IList GetParents(); } public class SomeClass diff --git a/Rhino.Mocks.Tests/Utilities/ReturnValueUtilTests.cs b/Rhino.Mocks.Tests/Utilities/ReturnValueUtilTests.cs index b093db63..a30342b8 100644 --- a/Rhino.Mocks.Tests/Utilities/ReturnValueUtilTests.cs +++ b/Rhino.Mocks.Tests/Utilities/ReturnValueUtilTests.cs @@ -26,9 +26,10 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion - +using System.Collections; using Xunit; using Rhino.Mocks.Utilities; +using System.Collections.Generic; namespace Rhino.Mocks.Tests.Utilities { @@ -38,6 +39,7 @@ public class ReturnValueUtilTests [Fact] public void DefaultReturnValue() { + Assert.Null(ReturnValueUtil.DefaultValue(typeof(void), null)); Assert.Null(ReturnValueUtil.DefaultValue(typeof (string),null)); Assert.Equal(0, ReturnValueUtil.DefaultValue(typeof (int),null)); Assert.Equal((short) 0, ReturnValueUtil.DefaultValue(typeof (short),null)); @@ -48,10 +50,63 @@ public void DefaultReturnValue() Assert.Equal(TestEnum.DefaultValue, ReturnValueUtil.DefaultValue(typeof (TestEnum),null)); } + [Fact] + public void DefaultReturnValue_WhenTheReturnTypeIsACollectionInterface_ReturnAnEmptyCollection() + { + Assert.NotNull(ReturnValueUtil.DefaultValue(typeof (IEnumerable), null) as IEnumerable); + + var defaultValueForCollections = ReturnValueUtil.DefaultValue(typeof (ICollection), null) as ICollection; + Assert.NotNull(defaultValueForCollections); + Assert.Equal(0, defaultValueForCollections.Count); + + var defaultValueForLists = ReturnValueUtil.DefaultValue(typeof (IList), null) as IList; + Assert.NotNull(defaultValueForLists); + Assert.Equal(0, defaultValueForLists.Count); + + var defaultValueForDictionaries = ReturnValueUtil.DefaultValue(typeof (IDictionary), null) as IDictionary; + Assert.NotNull(defaultValueForDictionaries); + Assert.Equal(0, defaultValueForDictionaries.Keys.Count); + Assert.Equal(0, defaultValueForDictionaries.Values.Count); + } + + [Fact] + public void DefaultReturnValue_WhenTheReturnTypeIsAGenericCollectionInterface_ReturnAnEmptyCollection() + { + var defValForGenericEnumerable = ReturnValueUtil.DefaultValue(typeof (IEnumerable), null); + Assert.NotNull(defValForGenericEnumerable); + + var defValForAGenericCollection = ReturnValueUtil.DefaultValue(typeof (ICollection), null) as ICollection; + Assert.NotNull(defValForAGenericCollection); + Assert.Equal(0, defValForAGenericCollection.Count); + + var defValForAGenericList = ReturnValueUtil.DefaultValue(typeof (IList), null) as IList; + Assert.NotNull(defValForAGenericList); + Assert.Equal(0, defValForAGenericList.Count); + + var defValForAGenericDictionary = + ReturnValueUtil.DefaultValue(typeof (IDictionary), null) as IDictionary; + Assert.NotNull(defValForAGenericDictionary); + Assert.Equal(0, defValForAGenericDictionary.Keys.Count); + Assert.Equal(0, defValForAGenericDictionary.Values.Count); + } + + [Fact] + public void DefaultReturnValue_WhenTheReturnTypeImplementsAdditionalsInterfacesToIEnumerable_ReturnNull() + { + object defVal = ReturnValueUtil.DefaultValue(typeof (IFoo), null); + + Assert.Null(defVal); + } + private enum TestEnum { DefaultValue, NonDefaultValue } + + public interface IFoo : IEnumerable + { + string Bar { get; set; } + } } } \ No newline at end of file diff --git a/Rhino.Mocks/Utilities/ReturnValueUtil.cs b/Rhino.Mocks/Utilities/ReturnValueUtil.cs index 327158d7..3ae59b99 100644 --- a/Rhino.Mocks/Utilities/ReturnValueUtil.cs +++ b/Rhino.Mocks/Utilities/ReturnValueUtil.cs @@ -28,6 +28,8 @@ using System; +using System.Collections; +using System.Collections.Generic; using Castle.Core.Interceptor; namespace Rhino.Mocks.Utilities @@ -39,7 +41,8 @@ public class ReturnValueUtil { /// /// The default value for a type. - /// Null for reference types and void + /// Empty Collections for IEnumerable, ICollection, IList and IDictionary types + /// Null for the rest of reference types and void /// 0 for value types. /// First element for enums /// Note that we need to get the value even for opened generic types, such as those from @@ -52,8 +55,40 @@ public static object DefaultValue(Type type, IInvocation invocation) { type = GenericsUtil.GetRealType(type, invocation); if (type.IsValueType == false || type==typeof(void)) - return null; + return TryInstantiateCollectionInterfacesOrReturnNull(type); + return Activator.CreateInstance(type); } + + private static object TryInstantiateCollectionInterfacesOrReturnNull(Type returnType) + { + if (!returnType.IsInterface) return null; + + if (returnType.IsGenericType) + { + Type[] arguments = returnType.GetGenericArguments(); + + Type typeDefinition = returnType.GetGenericTypeDefinition(); + + if (typeDefinition == typeof(IList<>) || + typeDefinition == typeof(IEnumerable<>) || + typeDefinition == typeof(ICollection<>)) + return Activator.CreateInstance(typeof(List<>).MakeGenericType(arguments)); + + if (typeDefinition == typeof(IDictionary<,>)) + return Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(arguments)); + + return null; + } + if (returnType == (typeof(IList)) || + returnType == typeof(IEnumerable) || + returnType.Equals(typeof(ICollection))) + return new ArrayList(); + + if (returnType.Equals(typeof(IDictionary))) + return new Dictionary(); + + return null; + } } } \ No newline at end of file