-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Skip data generation when already specified by inline or member attri…
…bute (#33) Skip data generation when data already specified through inline or member attribute
- Loading branch information
1 parent
55a3db2
commit 6136e10
Showing
6 changed files
with
203 additions
and
14 deletions.
There are no files selected for viewing
83 changes: 76 additions & 7 deletions
83
src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,106 @@ | ||
namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Attributes | ||
{ | ||
using System; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Reflection; | ||
using FluentAssertions; | ||
using global::AutoFixture; | ||
using global::AutoFixture.Xunit2; | ||
using Objectivity.AutoFixture.XUnit2.Core.Attributes; | ||
using Xunit; | ||
|
||
[Collection("AutoDataAdapterAttribute")] | ||
[Trait("Category", "Attributes")] | ||
[SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Test objects")] | ||
public class AutoDataAdapterAttributeTests | ||
{ | ||
[Fact(DisplayName = "GIVEN fixture WHEN constructor is invoked THEN passed fixture is being adapted")] | ||
public void GivenFixture_WhenConstructorIsInvoked_ThenPassedFixtureIsBeingAdapted() | ||
[Theory(DisplayName = "GIVEN fixture WHEN constructor is invoked THEN passed fixture is being adapted and inline values collection is empty")] | ||
[AutoData] | ||
public void GivenFixture_WhenConstructorIsInvoked_ThenPassedFixtureIsBeingAdaptedAndInlineValuesCollectionIsEmpty(Fixture fixture) | ||
{ | ||
// Arrange | ||
IFixture fixture = new Fixture(); | ||
|
||
// Act | ||
var attribute = new AutoDataAdapterAttribute(fixture); | ||
var attribute = new AutoDataAdapterAttribute(fixture, null); | ||
|
||
// Assert | ||
attribute.AdaptedFixture.Should().Be(fixture); | ||
attribute.InlineValues.Should().BeEmpty(); | ||
} | ||
|
||
[Fact(DisplayName = "GIVEN uninitialized fixture WHEN constructor is invoked THEN exception is thrown")] | ||
public void GivenUninitializedFixture_WhenConstructorIsInvoked_ThenExceptionIsThrown() | ||
{ | ||
// Arrange | ||
const IFixture fixture = null; | ||
// Act | ||
// Assert | ||
Assert.Throws<ArgumentNullException>(() => new AutoDataAdapterAttribute(null)); | ||
} | ||
|
||
[Theory(DisplayName = "GIVEN uninitialized method info WHEN GetData is invoked THEN exception is thrown")] | ||
[AutoData] | ||
public void GivenUninitializedMethodInfo_WhenConstructorIsInvoked_ThenExceptionIsThrown(Fixture fixture) | ||
{ | ||
// Arrange | ||
var attribute = new AutoDataAdapterAttribute(fixture); | ||
|
||
// Act | ||
// Assert | ||
Assert.Throws<ArgumentNullException>(() => attribute.GetData(null)); | ||
} | ||
|
||
[Fact(DisplayName = "GIVEN test data with instance WHEN GetData called THEN auto data generation skipped")] | ||
public void GivenTestDataWithInstance_WhenGetDataCalled_ThenAutoDataGenerationSkipped() | ||
{ | ||
// Arrange | ||
IFixture fixture = new Fixture(); | ||
var attribute = new AutoDataAdapterAttribute(fixture, SpecificTestClass.Create()); | ||
var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithAbstractTestClass), BindingFlags.Instance | BindingFlags.NonPublic); | ||
|
||
// Act | ||
var data = attribute.GetData(methodInfo).ToArray(); | ||
|
||
// Assert | ||
Assert.Throws<ArgumentNullException>(() => new AutoDataAdapterAttribute(fixture)); | ||
data.Should().HaveCount(1) | ||
.And.Subject.First().Should().HaveCount(methodInfo.GetParameters().Length) | ||
.And.NotContainNulls() | ||
.And.Subject.Skip(1).Should().AllBeEquivalentTo(data.First().Last()); | ||
} | ||
|
||
[Fact(DisplayName = "GIVEN empty test data WHEN GetData called THEN auto data generation throws exception")] | ||
public void GivenEmptyTestData_WhenGetDataCalled_ThenAutoDataGenerationSkipped() | ||
{ | ||
// Arrange | ||
IFixture fixture = new Fixture(); | ||
var attribute = new AutoDataAdapterAttribute(fixture); | ||
var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithAbstractTestClass), BindingFlags.Instance | BindingFlags.NonPublic); | ||
|
||
// Act | ||
Action data = () => attribute.GetData(methodInfo); | ||
|
||
// Assert | ||
data.Should().Throw<Exception>(); | ||
} | ||
|
||
protected string TestMethodWithAbstractTestClass(SpecificTestClass instance, [Frozen]string text, string message) | ||
{ | ||
return $"{instance}: {text}, {message}"; | ||
} | ||
|
||
public abstract class AbstractTestClass | ||
{ | ||
} | ||
|
||
public class SpecificTestClass : AbstractTestClass | ||
{ | ||
private SpecificTestClass() | ||
{ | ||
} | ||
|
||
public static AbstractTestClass Create() | ||
{ | ||
return new SpecificTestClass(); | ||
} | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/Objectivity.AutoFixture.XUnit2.Core.Tests/Comparers/CustomizeAttributeComparerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Comparers | ||
{ | ||
using System.Collections.Generic; | ||
using FluentAssertions; | ||
using global::AutoFixture; | ||
using global::AutoFixture.Xunit2; | ||
using Objectivity.AutoFixture.XUnit2.Core.Attributes; | ||
using Objectivity.AutoFixture.XUnit2.Core.Comparers; | ||
using Objectivity.AutoFixture.XUnit2.Core.Customizations; | ||
using Xunit; | ||
|
||
public class CustomizeAttributeComparerTests | ||
{ | ||
private static readonly CustomizeAttributeComparer Comparer = new CustomizeAttributeComparer(); | ||
private static readonly CustomizeWithAttribute CustomizeAttribute = new CustomizeWithAttribute(typeof(DoNotThrowOnRecursionCustomization)); | ||
private static readonly FrozenAttribute FrozenAttribute = new FrozenAttribute(); | ||
|
||
public static IEnumerable<object[]> TestData { get; } = new[] | ||
{ | ||
new object[] { CustomizeAttribute, CustomizeAttribute, 0 }, | ||
new object[] { FrozenAttribute, FrozenAttribute, 0 }, | ||
new object[] { FrozenAttribute, CustomizeAttribute, 1 }, | ||
new object[] { CustomizeAttribute, FrozenAttribute, -1 }, | ||
}; | ||
|
||
[Theory(DisplayName = "GIVEN both attributes WHEN Compare is invoked THEN expected result returned")] | ||
[MemberData(nameof(TestData))] | ||
public void GivenBothNonFrozenAttributes_WhenCompareIsInvoked_ThenBothEquals( | ||
IParameterCustomizationSource x, | ||
IParameterCustomizationSource y, | ||
int expectedResult) | ||
{ | ||
// Arrange | ||
// Act | ||
var result = Comparer.Compare(x, y); | ||
|
||
// Assert | ||
result.Should().Be(expectedResult); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 50 additions & 1 deletion
51
src/Objectivity.AutoFixture.XUnit2.Core/Attributes/AutoDataAdapterAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,68 @@ | ||
namespace Objectivity.AutoFixture.XUnit2.Core.Attributes | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using global::AutoFixture; | ||
using global::AutoFixture.Kernel; | ||
using global::AutoFixture.Xunit2; | ||
using Objectivity.AutoFixture.XUnit2.Core.Common; | ||
using Objectivity.AutoFixture.XUnit2.Core.Comparers; | ||
|
||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | ||
internal sealed class AutoDataAdapterAttribute : AutoDataAttribute | ||
{ | ||
public AutoDataAdapterAttribute(IFixture fixture) | ||
public AutoDataAdapterAttribute(IFixture fixture, params object[] inlineValues) | ||
: base(() => fixture) | ||
{ | ||
this.AdaptedFixture = fixture.NotNull(nameof(fixture)); | ||
this.InlineValues = inlineValues ?? Array.Empty<object>(); | ||
} | ||
|
||
public IFixture AdaptedFixture { get; } | ||
|
||
public IReadOnlyCollection<object> InlineValues { get; } | ||
|
||
public override IEnumerable<object[]> GetData(MethodInfo testMethod) | ||
{ | ||
if (testMethod == null) | ||
{ | ||
throw new ArgumentNullException(nameof(testMethod)); | ||
} | ||
|
||
// This is an extension of AutoDataAttribute.GetData method | ||
// with the ability to skip already provided inline values | ||
var specimens = new List<object>(this.InlineValues); | ||
var parameters = testMethod.GetParameters(); | ||
foreach (var p in parameters.Skip(this.InlineValues.Count)) | ||
{ | ||
this.CustomizeFixture(p); | ||
|
||
var specimen = this.Resolve(p); | ||
specimens.Add(specimen); | ||
} | ||
|
||
return new[] { specimens.ToArray() }; | ||
} | ||
|
||
private void CustomizeFixture(ParameterInfo p) | ||
{ | ||
var customizeAttributes = p.GetCustomAttributes() | ||
.OfType<IParameterCustomizationSource>() | ||
.OrderBy(x => x, new CustomizeAttributeComparer()); | ||
|
||
foreach (var ca in customizeAttributes) | ||
{ | ||
var c = ca.GetCustomization(p); | ||
this.AdaptedFixture.Customize(c); | ||
} | ||
} | ||
|
||
private object Resolve(ParameterInfo p) | ||
{ | ||
var context = new SpecimenContext(this.AdaptedFixture); | ||
return context.Resolve(p); | ||
} | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/Objectivity.AutoFixture.XUnit2.Core/Comparers/CustomizeAttributeComparer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
namespace Objectivity.AutoFixture.XUnit2.Core.Comparers | ||
{ | ||
using System.Collections.Generic; | ||
using global::AutoFixture; | ||
using global::AutoFixture.Xunit2; | ||
|
||
// Direct copy from the AutoFixture source code as the original class is internal. | ||
internal class CustomizeAttributeComparer : Comparer<IParameterCustomizationSource> | ||
{ | ||
public override int Compare(IParameterCustomizationSource x, IParameterCustomizationSource y) | ||
{ | ||
var xFrozen = x is FrozenAttribute; | ||
var yFrozen = y is FrozenAttribute; | ||
|
||
if (xFrozen && !yFrozen) | ||
{ | ||
return 1; | ||
} | ||
|
||
if (yFrozen && !xFrozen) | ||
{ | ||
return -1; | ||
} | ||
|
||
return 0; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters