/// <summary> /// Prepares the mocks, dependencies and the item under test. /// </summary> /// <exception cref="LeapingGorilla.Testing.Exceptions.NoItemUnderTestException">Thrown if the user has specified [Dependency]'s but there is no [ItemUnderTest]</exception> /// <exception cref="NoMatchingConstructorFoundException">Thrown if no constructor can be found on the [ItemUnderTest] that matches the number of [Dependency] fields/properties</exception> /// <exception cref="DependencyMismatchException">Thrown if a [Dependency] cannot be found for a constructor parameter when one is expected.</exception> /// <exception cref="NoItemUnderTestException">Thrown if there are fields/properties marked with [Dependency] but none with [ItemUnderTest]</exception> private void PrepareMocksDependenciesAndItemUnderTest() { // Create a fast accessor onto the test class var accessor = TypeAccessor.Create(GetType(), true); // Get and validate the item under test var itemUnderTest = GetPropertiesWithAttribute(typeof(ItemUnderTestAttribute)).Select(pi => new { Type = pi.PropertyType, pi.Name }) .Concat(GetFieldsWithAttribute(typeof(ItemUnderTestAttribute)).Select(pi => new { Type = pi.FieldType, pi.Name })) .FirstOrDefault(); // Create mocks and dependencies CreateManualDependencies(); var dependencies = CreateAndAssignPropertiesOrFieldsWithAttribute(accessor, typeof(DependencyAttribute)); dependencies.AddRange(CreateAndAssignPropertiesOrFieldsWithAttribute(accessor, typeof(NullDependencyAttribute))); CreateAndAssignPropertiesOrFieldsWithAttribute(accessor, typeof(MockAttribute)); // It is invalid to have dependencies but no item under test if (itemUnderTest == null) { if (dependencies.Count > 0) { throw new NoItemUnderTestException(); } // No item under test but no dependencies. We can abort out of the rest of the setup return; } // Determine the correct constructor for the item under test. If we cant match one then error out var constructor = GetPreferredConstructor(itemUnderTest.Type, dependencies); if (constructor == null) { throw new NoMatchingConstructorFoundException(dependencies); } var parameters = constructor.GetParameters(); var constructorArguments = new object[parameters.Length]; // Assign parameters. We remove them from the dependencies list as we go so we don't re-use dependencies in case of indirect (type only) matching for (int index = 0; index < parameters.Length; index++) { var param = parameters[index]; var dep = dependencies.FirstOrDefault(d => d.Name == param.Name && d.Type == param.ParameterType) ?? dependencies.FirstOrDefault(d => d.Type == param.ParameterType); if (dep == null) { throw new DependencyMismatchException(param.ParameterType, param.Name, index, parameters); } dependencies.Remove(dep); constructorArguments[index] = dep.Value; } // Create the item under test and load it into the test class accessor[this, itemUnderTest.Name] = Activator.CreateInstance(itemUnderTest.Type, constructorArguments); }
private List <Dependency> CreateAndAssignPropertiesOrFieldsWithAttribute(TypeAccessor accessor, Type attributeType) { var dependencies = BuildDependenciesForPropertiesOrFieldsWithAttributeType(attributeType); foreach (var dep in dependencies) { accessor[this, dep.Name] = dep.Value; } return(dependencies); }