PageRenderTime 34ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/RELEASES/3.0 RC1/Source/NMock2/Internal/MockBuilder.cs

#
C# | 233 lines | 107 code | 33 blank | 93 comment | 15 complexity | 894c6048c494242732d6ff0a81956bb5 MD5 | raw file
Possible License(s): Apache-2.0
  1. //-----------------------------------------------------------------------
  2. // <copyright file="MockBuilder.cs" company="NMock2">
  3. //
  4. // http://www.sourceforge.net/projects/NMock2
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License");
  7. // you may not use this file except in compliance with the License.
  8. // You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. // </copyright>
  18. //-----------------------------------------------------------------------
  19. namespace NMock2.Internal
  20. {
  21. using System;
  22. using System.Collections.Generic;
  23. using System.Reflection;
  24. using Matchers;
  25. using Monitoring;
  26. using Syntax;
  27. /// <summary>
  28. /// Allows a mock object to be incrementally defined, and then finally created.
  29. /// </summary>
  30. public class MockBuilder : IMockDefinitionSyntax
  31. {
  32. /// <summary>
  33. /// A single empty array instance that is used as a default value
  34. /// for constructor arguments.
  35. /// </summary>
  36. private static readonly object[] EmptyArgsArray = new object[0];
  37. /// <summary>
  38. /// The name of the mock object. Null is a valid value.
  39. /// </summary>
  40. private string name = null;
  41. /// <summary>
  42. /// The types that the mock object needs to implement.
  43. /// </summary>
  44. private List<Type> types = new List<Type>();
  45. /// <summary>
  46. /// Constructor arguments for any class type that this mock might subclass.
  47. /// If not subclassing, or if using a default constructor, then this should
  48. /// be an empty array.
  49. /// </summary>
  50. private object[] constructorArgs = EmptyArgsArray;
  51. /// <summary>
  52. /// The MockStyle for the mock. If not specified, this will ultimately be
  53. /// assumed to be MockStyle.Default.
  54. /// </summary>
  55. private MockStyle? mockStyle = null;
  56. #region IMockDefinitionSyntax Members
  57. /// <summary>
  58. /// Specifies a type that this mock should implement. This may be a class or interface,
  59. /// but there can only be a maximum of one class implemented by a mock.
  60. /// </summary>
  61. /// <typeparam name="T">The type to implement.</typeparam>
  62. /// <returns>The mock object definition.</returns>
  63. public IMockDefinitionSyntax Implementing<T>()
  64. {
  65. this.types.Add(typeof(T));
  66. return this;
  67. }
  68. /// <summary>
  69. /// Specifies the types that this mock should implement. These may be a class or interface,
  70. /// but there can only be a maximum of one class implemented by a mock.
  71. /// </summary>
  72. /// <param name="types">The types to implement.</param>
  73. /// <returns>The mock object definition.</returns>
  74. public IMockDefinitionSyntax Implementing(params Type[] types)
  75. {
  76. this.types.AddRange(types);
  77. return this;
  78. }
  79. /// <summary>
  80. /// Specifies how the mock object should behave when first created.
  81. /// It is invalid to set the MockStyle of a mock more than once.
  82. /// </summary>
  83. /// <param name="style">A MockStyle value.</param>
  84. /// <returns>The mock object definition.</returns>
  85. public IMockDefinitionSyntax OfStyle(MockStyle style)
  86. {
  87. if (this.mockStyle.HasValue)
  88. {
  89. throw new InvalidOperationException("MockStyle has already been set for this mock definition.");
  90. }
  91. this.mockStyle = style;
  92. return this;
  93. }
  94. /// <summary>
  95. /// Specifies the arguments for the constructor of the class to be mocked.
  96. /// Only applicable when mocking a class with a non-default constructor.
  97. /// It is invalid to specify the constructor arguments of a mock more than once.
  98. /// </summary>
  99. /// <param name="args">The arguments for the class constructor.</param>
  100. /// <returns>The mock object definition.</returns>
  101. public IMockDefinitionSyntax WithArgs(params object[] args)
  102. {
  103. if (this.constructorArgs != EmptyArgsArray)
  104. {
  105. throw new InvalidOperationException("Constructor arguments have already been specified for this mock definition.");
  106. }
  107. this.constructorArgs = args;
  108. return this;
  109. }
  110. /// <summary>
  111. /// Specifies a name for the mock. This will be used in error messages,
  112. /// and as the return value of ToString() if not mocking a class.
  113. /// It is invalid to specify the name of a mock more than once.
  114. /// </summary>
  115. /// <param name="name">The name for the mock.</param>
  116. /// <returns>The mock object definition.</returns>
  117. public IMockDefinitionSyntax Named(string name)
  118. {
  119. if (this.name != null)
  120. {
  121. throw new InvalidOperationException("A name has already been specified for this mock definition.");
  122. }
  123. this.name = name;
  124. return this;
  125. }
  126. #endregion
  127. #region IMockDefinition Members
  128. /// <summary>
  129. /// This method supports NMock2 infrastructure and is not intended to be called directly from your code.
  130. /// </summary>
  131. /// <param name="primaryType">The primary type that is being mocked.</param>
  132. /// <param name="mockery">The current <see cref="Mockery"/> instance.</param>
  133. /// <param name="mockObjectFactory">An <see cref="IMockObjectFactory"/> to use when creating the mock.</param>
  134. /// <returns>A new mock instance.</returns>
  135. public object Create(Type primaryType, Mockery mockery, IMockObjectFactory mockObjectFactory)
  136. {
  137. if (this.name == null)
  138. {
  139. this.name = this.DefaultNameFor(primaryType);
  140. }
  141. CompositeType compositeType = new CompositeType(primaryType, this.types.ToArray());
  142. if (compositeType.PrimaryType.IsInterface)
  143. {
  144. if (this.constructorArgs.Length > 0)
  145. {
  146. throw new InvalidOperationException("Cannot specify constructor arguments when mocking an interface");
  147. }
  148. CheckInterfacesDoNotContainToStringMethodDeclaration(compositeType);
  149. }
  150. return mockObjectFactory.CreateMock(
  151. mockery,
  152. compositeType,
  153. this.name,
  154. this.mockStyle ?? MockStyle.Default,
  155. this.constructorArgs);
  156. }
  157. #endregion
  158. /// <summary>
  159. /// Returns the default name for a type that is used to name mocks.
  160. /// </summary>
  161. /// <param name="type">The type to get the default name for.</param>
  162. /// <returns>Default name for the specified type.</returns>
  163. protected virtual string DefaultNameFor(Type type)
  164. {
  165. string name = type.Name;
  166. int firstLower = FirstLowerCaseChar(name);
  167. return firstLower == name.Length ?
  168. name.ToLower() :
  169. name.Substring(firstLower - 1, 1).ToLower() + name.Substring(firstLower);
  170. }
  171. /// <summary>
  172. /// Finds the first lower case char in the specified string.
  173. /// </summary>
  174. /// <param name="s">The string to inspect.</param>
  175. /// <returns>the first lower case char in the specified string.</returns>
  176. private static int FirstLowerCaseChar(string s)
  177. {
  178. int i = 0;
  179. while (i < s.Length && !Char.IsLower(s[i]))
  180. {
  181. i++;
  182. }
  183. return i;
  184. }
  185. /// <summary>
  186. /// Checks that interfaces do not contain ToString method declarations.
  187. /// </summary>
  188. /// <param name="mockedTypes">The types that are to be mocked.</param>
  189. private static void CheckInterfacesDoNotContainToStringMethodDeclaration(CompositeType mockedTypes)
  190. {
  191. foreach (MethodInfo method in mockedTypes.GetMatchingMethods(new MethodNameMatcher("ToString"), false))
  192. {
  193. if (method.ReflectedType.IsInterface && method.GetParameters().Length == 0)
  194. {
  195. throw new ArgumentException("Interfaces must not contain a declaration for ToString().");
  196. }
  197. }
  198. }
  199. }
  200. }