Skip to content

Commit

Permalink
Skip data generation when already specified by inline or member attri…
Browse files Browse the repository at this point in the history
…bute (#33)

Skip data generation when data already specified through inline or member attribute
  • Loading branch information
piotrzajac authored Aug 3, 2020
1 parent 55a3db2 commit 6136e10
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 14 deletions.
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();
}
}
}
}
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Providers
{
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using FluentAssertions;
using global::AutoFixture;
using global::AutoFixture.Xunit2;
Expand All @@ -16,17 +15,20 @@ public class InlineAutoDataAttributeProviderTests
[Theory(DisplayName = "GIVEN initialized fixture WHEN GetAttribute is invoked THEN attribute with specified fixture is returned")]
[AutoData]
[SuppressMessage("ReSharper", "PossibleNullReferenceException", Justification = "Assertion checks it earlier and throws exception.")]
public void GivenInitializedFixture_WhenGetAttributeIsInvoked_ThenAttributeWithSpecifiedFixtureIsReturned(Fixture fixture)
public void GivenInitializedFixture_WhenGetAttributeIsInvoked_ThenAttributeWithSpecifiedFixtureIsReturned(
Fixture fixture,
object[] inlineValues)
{
// Arrange
var provider = new InlineAutoDataAttributeProvider();

// Act
var dataAttribute = provider.GetAttribute(fixture) as CompositeDataAttribute;
var attribute = provider.GetAttribute(fixture, inlineValues);

// Assert
dataAttribute.Should().NotBeNull();
dataAttribute.Attributes.FirstOrDefault(a => a is AutoDataAdapterAttribute).As<AutoDataAdapterAttribute>().AdaptedFixture.Should().Be(fixture);
var autoDataAdapterAttribute = attribute.Should().BeOfType<AutoDataAdapterAttribute>().Which;
autoDataAdapterAttribute.AdaptedFixture.Should().Be(fixture);
autoDataAdapterAttribute.InlineValues.Should().BeEquivalentTo(inlineValues);
}
}
}
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);
}
}
}
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public sealed class InlineAutoDataAttributeProvider : IAutoFixtureInlineAttribut
{
public DataAttribute GetAttribute(IFixture fixture, params object[] values)
{
return new CompositeDataAttribute(new InlineDataAttribute(values), new AutoDataAdapterAttribute(fixture));
return new AutoDataAdapterAttribute(fixture, values);
}
}
}

0 comments on commit 6136e10

Please sign in to comment.