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

/Src/AutoFixture/Fixture.cs

http://autofixture.codeplex.com
C# | 297 lines | 121 code | 25 blank | 151 comment | 9 complexity | e4faeb0f6c8a4e6fe9447f2dfa70fa66 MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Ploeh.AutoFixture.Dsl;
  5. using Ploeh.AutoFixture.Kernel;
  6. namespace Ploeh.AutoFixture
  7. {
  8. /// <summary>
  9. /// Provides anonymous object creation services.
  10. /// </summary>
  11. public class Fixture : IFixture
  12. {
  13. private readonly List<ISpecimenBuilderTransformation> behaviors;
  14. private readonly CompositeSpecimenBuilder customizer;
  15. private readonly ISpecimenBuilder engine;
  16. private readonly CompositeSpecimenBuilder residueCollector;
  17. private readonly IMultiple multiple;
  18. /// <summary>
  19. /// Initializes a new instance of the <see cref="Fixture"/> class.
  20. /// </summary>
  21. public Fixture()
  22. : this(new DefaultEngineParts())
  23. {
  24. }
  25. /// <summary>
  26. /// Initializes a new instance of the <see cref="Fixture"/> class with the supplied engine
  27. /// parts.
  28. /// </summary>
  29. /// <param name="engineParts">The engine parts.</param>
  30. public Fixture(DefaultRelays engineParts)
  31. : this(new CompositeSpecimenBuilder(engineParts), engineParts)
  32. {
  33. }
  34. /// <summary>
  35. /// Initializes a new instance of the <see cref="Fixture"/> class with the supplied engine
  36. /// and a definition of what 'many' means.
  37. /// </summary>
  38. /// <param name="engine">The engine.</param>
  39. /// <param name="multiple">The definition and implementation of 'many'.</param>
  40. public Fixture(ISpecimenBuilder engine, IMultiple multiple)
  41. {
  42. if (engine == null)
  43. {
  44. throw new ArgumentNullException("engine");
  45. }
  46. if (multiple == null)
  47. {
  48. throw new ArgumentNullException("multiple");
  49. }
  50. this.customizer = new CompositeSpecimenBuilder();
  51. this.engine = engine;
  52. this.residueCollector = new CompositeSpecimenBuilder();
  53. this.Customizations.Add(new FilteringSpecimenBuilder(new MethodInvoker(new ModestConstructorQuery()), new NullableEnumRequestSpecification()));
  54. this.Customizations.Add(new EnumGenerator());
  55. this.multiple = multiple;
  56. this.behaviors = new List<ISpecimenBuilderTransformation>();
  57. this.behaviors.Add(new ThrowingRecursionBehavior());
  58. }
  59. /// <summary>
  60. /// Gets the behaviors that are applied when <see cref="Compose"/> is invoked.
  61. /// </summary>
  62. public IList<ISpecimenBuilderTransformation> Behaviors
  63. {
  64. get { return this.behaviors; }
  65. }
  66. /// <summary>
  67. /// Gets the customizations that intercept the <see cref="Engine"/>.
  68. /// </summary>
  69. /// <remarks>
  70. /// <para>
  71. /// Any <see cref="ISpecimenBuilder"/> in this list are invoked before
  72. /// <see cref="Engine"/>, giving them a chance to intercept a request and resolve it before
  73. /// the Engine.
  74. /// </para>
  75. /// <para>
  76. /// <see cref="Customize{T}"/> places resulting customizations in this list.
  77. /// </para>
  78. /// </remarks>
  79. /// <seealso cref="Engine"/>
  80. /// <seealso cref="ResidueCollectors"/>
  81. public IList<ISpecimenBuilder> Customizations
  82. {
  83. get { return this.customizer.Builders; }
  84. }
  85. /// <summary>
  86. /// Gets the core engine of the <see cref="Fixture"/> instance.
  87. /// </summary>
  88. /// <remarks>
  89. /// <para>
  90. /// This is the core engine that drives a <see cref="Fixture"/> instance. Even with no
  91. /// <see cref="Customizations"/> or <see cref="ResidueCollectors"/>, the
  92. /// <see cref="Engine"/> should be capably of resolving a wide range of different requests,
  93. /// based on conventions.
  94. /// </para>
  95. /// </remarks>
  96. /// <see cref="Customizations"/>
  97. /// <see cref="ResidueCollectors"/>
  98. public ISpecimenBuilder Engine
  99. {
  100. get { return this.engine; }
  101. }
  102. /// <summary>
  103. /// Gets or sets if writable properties should generally be assigned a value when
  104. /// generating an anonymous object.
  105. /// </summary>
  106. /// <remarks>
  107. /// <para>
  108. /// The default value is false.
  109. /// </para>
  110. /// </remarks>
  111. public bool OmitAutoProperties { get; set; }
  112. /// <summary>
  113. /// Gets or sets a number that controls how many objects are created when a
  114. /// <see cref="Fixture"/> creates more than one anonymous objects.
  115. /// </summary>
  116. /// <remarks>
  117. /// <para>
  118. /// The default value is 3.
  119. /// </para>
  120. /// </remarks>
  121. /// <seealso cref="CollectionFiller.AddManyTo{T}(IFixture, ICollection{T})" />
  122. /// <seealso cref="CollectionFiller.AddManyTo{T}(IFixture, ICollection{T}, Func{T})" />
  123. /// <seealso cref="SpecimenFactory.CreateMany{T}(ISpecimenBuilderComposer)" />
  124. /// <seealso cref="SpecimenFactory.CreateMany{T}(ISpecimenBuilderComposer, int)" />
  125. /// <seealso cref="Repeat"/>
  126. public int RepeatCount
  127. {
  128. get { return this.multiple.Count; }
  129. set { this.multiple.Count = value; }
  130. }
  131. /// <summary>
  132. /// Gets the residue collectors that can be used to handle requests that neither the
  133. /// <see cref="Customizations"/> nor <see cref="Engine"/> could handle.
  134. /// </summary>
  135. /// <remarks>
  136. /// <para>
  137. /// These <see cref="ISpecimenBuilder"/> instances will be invoked if no previous builder
  138. /// could resolve a request. This gives you the opportunity to define fallback strategies
  139. /// to deal with unresolved requests.
  140. /// </para>
  141. /// </remarks>
  142. public IList<ISpecimenBuilder> ResidueCollectors
  143. {
  144. get { return this.residueCollector.Builders; }
  145. }
  146. /// <summary>
  147. /// Customizes the creation algorithm for a single object, effectively turning off all
  148. /// Customizations on the <see cref="IFixture"/>.
  149. /// </summary>
  150. /// <typeparam name="T">
  151. /// The type of object for which the algorithm should be customized.
  152. /// </typeparam>
  153. /// <returns>
  154. /// A <see cref="ICustomizationComposer{T}"/> that can be used to customize the creation
  155. /// algorithm before creating the object.
  156. /// </returns>
  157. /// <remarks>
  158. /// <para>
  159. /// The Build method kicks off a Fluent API which is usually completed by invoking
  160. /// <see cref="SpecimenFactory.CreateAnonymous{T}(IPostprocessComposer{T})"/> on the method
  161. /// chain.
  162. /// </para>
  163. /// <para>
  164. /// Note that the Build method chain is best understood as a one-off Customization. It
  165. /// bypasses all Customizations on the <see cref="Fixture"/> instance. Instead, it allows
  166. /// fine-grained control when building a specific specimen. However, in most cases, adding
  167. /// a convention-based <see cref="ICustomization"/> is a better, more flexible option.
  168. /// </para>
  169. /// </remarks>
  170. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Although this CA warning should never be suppressed, this particular usage scenario has been discussed and accepted on the FxCop DL.")]
  171. public ICustomizationComposer<T> Build<T>()
  172. {
  173. return new CompositeComposer<T>(
  174. new BehaviorComposer<T>(
  175. new Composer<T>().WithAutoProperties(this.EnableAutoProperties),
  176. this.Behaviors),
  177. new NullComposer<T>(this.Compose));
  178. }
  179. /// <summary>
  180. /// Applies a customization.
  181. /// </summary>
  182. /// <param name="customization">The customization to apply.</param>
  183. /// <returns>
  184. /// The current instance.
  185. /// </returns>
  186. public IFixture Customize(ICustomization customization)
  187. {
  188. if (customization == null)
  189. {
  190. throw new ArgumentNullException("customization");
  191. }
  192. customization.Customize(this);
  193. return this;
  194. }
  195. /// <summary>
  196. /// Customizes the creation algorithm for all objects of a given type.
  197. /// </summary>
  198. /// <typeparam name="T">The type of object to customize.</typeparam>
  199. /// <param name="composerTransformation">
  200. /// A function that customizes a given <see cref="ICustomizationComposer{T}"/> and returns
  201. /// the modified composer.
  202. /// </param>
  203. /// <remarks>
  204. /// <para>
  205. /// The resulting <see cref="ISpecimenBuilder"/> is added to <see cref="Customizations"/>.
  206. /// </para>
  207. /// </remarks>
  208. public void Customize<T>(Func<ICustomizationComposer<T>, ISpecimenBuilderComposer> composerTransformation)
  209. {
  210. if (composerTransformation == null)
  211. {
  212. throw new ArgumentNullException("composerTransformation");
  213. }
  214. var c = composerTransformation(new Composer<T>().WithAutoProperties(this.EnableAutoProperties));
  215. this.customizer.Builders.Insert(0, c.Compose());
  216. }
  217. /// <summary>
  218. /// Repeats a function many times.
  219. /// </summary>
  220. /// <typeparam name="T">
  221. /// The type of object that <paramref name="function"/> creates.
  222. /// </typeparam>
  223. /// <param name="function">
  224. /// A function that creates an instance of <typeparamref name="T"/>.
  225. /// </param>
  226. /// <returns>A sequence of objects created by <paramref name="function"/>.</returns>
  227. /// <remarks>
  228. /// <para>
  229. /// The number of times <paramref name="function"/> is invoked is determined by
  230. /// <see cref="RepeatCount"/>.
  231. /// </para>
  232. /// </remarks>
  233. public IEnumerable<T> Repeat<T>(Func<T> function)
  234. {
  235. return from f in Enumerable.Repeat(function, this.RepeatCount)
  236. select f();
  237. }
  238. /// <summary>
  239. /// Composes a new <see cref="ISpecimenBuilder"/> instance that contains all the relevant
  240. /// strategies defined for this instance.
  241. /// </summary>
  242. /// <returns>
  243. /// A new <see cref="ISpecimenBuilder"/> instance that contains all the relevant strategies
  244. /// for the this <see cref="Fixture"/> instance, including <see cref="Customizations"/>,
  245. /// <see cref="Engine"/> and <see cref="ResidueCollectors"/>.
  246. /// </returns>
  247. public ISpecimenBuilder Compose()
  248. {
  249. var builder = this.Engine;
  250. if (this.EnableAutoProperties)
  251. {
  252. builder = new Postprocessor(
  253. builder,
  254. new AutoPropertiesCommand().Execute,
  255. new AnyTypeSpecification());
  256. }
  257. builder = new CompositeSpecimenBuilder(
  258. this.customizer,
  259. builder,
  260. this.residueCollector,
  261. new TerminatingSpecimenBuilder());
  262. return this.Behaviors.Aggregate(
  263. builder,
  264. (b, behavior) =>
  265. behavior.Transform(b));
  266. }
  267. private bool EnableAutoProperties
  268. {
  269. get { return !this.OmitAutoProperties; }
  270. }
  271. }
  272. }