From 64b15f8cc1767ba9e4f06ce26a1345d34f59cbdf Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Wed, 22 Apr 2026 07:51:41 +0200 Subject: [PATCH 1/3] refactor(tests): consolidate duplicate Facts into Theories Replace pairs of [Fact] methods that tested the same behaviour with different inputs with a single [Theory] using [InlineData] or [MemberData]/TheoryData. Affected files: RandomFixedValuesGeneratorTests, RandomExceptValuesGeneratorTests, ValuesRequestTests, RequestFactoryRelayTests, and EnumerableExtensionsTests. Co-Authored-By: Claude Sonnet 4.6 --- ...nsolidate-duplicate-Facts-into-Theories.md | 19 + dotnet-tools.json | 2 +- .../Common/EnumerableExtensionsTests.cs | 235 +++++----- .../Requests/ValuesRequestTests.cs | 32 +- .../RandomExceptValuesGeneratorTests.cs | 158 ++++--- .../RandomFixedValuesGeneratorTests.cs | 236 +++++----- .../RequestFactoryRelayTests.cs | 403 +++++++++--------- 7 files changed, 531 insertions(+), 554 deletions(-) create mode 100644 .backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md diff --git a/.backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md b/.backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md new file mode 100644 index 00000000..0358623b --- /dev/null +++ b/.backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md @@ -0,0 +1,19 @@ +--- +id: TASK-17 +title: Consolidate duplicate Facts into Theories +status: Done +assignee: + - claude + - piotrzajac +created_date: '2026-04-21 08:21' +updated_date: '2026-04-21 11:06' +labels: [] +dependencies: [] +priority: low +--- + +## Acceptance Criteria + +- [x] #1 Identify all [Fact] methods across the all test projects that test the same behavior with different inputs +- [x] #2 Replace duplicate Facts with a single [Theory] using [InlineData] or [MemberData]/[TheoryData] as appropriate + diff --git a/dotnet-tools.json b/dotnet-tools.json index 5ead7f07..cda38d40 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -17,7 +17,7 @@ "rollForward": true }, "dotnet-stryker": { - "version": "4.14.0", + "version": "4.14.1", "commands": [ "dotnet-stryker" ], diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Common/EnumerableExtensionsTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Common/EnumerableExtensionsTests.cs index 625eb51f..d3bd151e 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Common/EnumerableExtensionsTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Common/EnumerableExtensionsTests.cs @@ -1,121 +1,114 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Common -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - - using global::AutoFixture.Xunit2; - - using Objectivity.AutoFixture.XUnit2.Core.Common; - - using Xunit; - - [Collection("EnumerableExtensions")] - [Trait("Category", "Common")] - public class EnumerableExtensionsTests - { - [Fact(DisplayName = "GIVEN uninitialized argument WHEN TryGetEnumerableSingleTypeArgument is invoked THEN exception is thrown")] - [SuppressMessage("ReSharper", "UnusedVariable", Justification = "This is good enougth to test the logic.")] - public void GivenUninitializedArgument_WhenTryGetEnumerableSingleTypeArgumentIsInvoked_ThenExceptionIsThrown() - { - // Arrange - Type enumerableType = null; - - // Act - object Act() => enumerableType.TryGetEnumerableSingleTypeArgument(out _); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("type", exception.ParamName); - } - - [InlineData(typeof(int[]), typeof(int))] - [InlineData(typeof(List), typeof(int))] - [InlineData(typeof(Dictionary), typeof(KeyValuePair))] - [InlineData(typeof(IEnumerable), typeof(int))] - [Theory(DisplayName = "GIVEN valid collection WHEN TryGetEnumerableSingleTypeArgument is invoked THEN collection single type argument returned")] - public void GivenValidCollection_WhenTryGetEnumerableSingleTypeArgumentIsInvoked_ThenCollectionSingleTypeArgumentReturned(Type enumerableType, Type expectedType) - { - // Arrange - // Act - var isSuccessful = enumerableType.TryGetEnumerableSingleTypeArgument(out var itemType); - - // Assert - Assert.True(isSuccessful); - Assert.Equal(expectedType, itemType); - } - - [InlineData(typeof(Tuple))] - [InlineData(typeof(IEnumerable))] - [Theory(DisplayName = "GIVEN invalid collection WHEN TryGetEnumerableSingleTypeArgument is invoked THEN no argument returned")] - public void GivenInvalidCollection_WhenTryGetEnumerableSingleTypeArgumentIsInvoked_ThenNoArgumentReturned(Type enumerableType) - { - // Arrange - // Act - var isSuccessful = enumerableType.TryGetEnumerableSingleTypeArgument(out _); - - // Assert - Assert.False(isSuccessful); - } - - [Fact(DisplayName = "GIVEN generic definition collection WHEN TryGetEnumerableSingleTypeArgument is invoked THEN no argument returned")] - public void GivenGenericDefinitionCollection_WhenTryGetEnumerableSingleTypeArgumentIsInvoked_ThenNoArgumentReturned() - { - // Arrange - var enumerableType = typeof(IEnumerable).GetGenericTypeDefinition(); - - // Act - var isSuccessful = enumerableType.TryGetEnumerableSingleTypeArgument(out _); - - // Assert - Assert.False(isSuccessful); - } - - [InlineData(null, typeof(int), "items")] - [InlineData(new[] { 1 }, null, "itemType")] - [Theory(DisplayName = "GIVEN uninitialized argument WHEN ToTypedArray is invoked THEN exception is thrown")] - public void GivenUninitializedArgument_WhenToTypedArrayIsInvoked_ThenExceptionIsThrown( - IEnumerable items, - Type itemType, - string exceptionParamName) - { - // Arrange - // Act - object Act() => items.ToTypedArray(itemType); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal(exceptionParamName, exception.ParamName); - } - - [AutoData] - [Theory(DisplayName = "GIVEN typed enumerable WHEN ToTypedArray is invoked THEN array is returned")] - public void GivenTypedEnumerable_WhenToTypedArrayIsInvoked_ThenArrayIsReturned(int[] items) - { - // Arrange - var itemType = typeof(int); - - // Act - var array = items.ToTypedArray(itemType); - - // Assert - Assert.Equal(items, array); - Assert.True(array.GetType().IsArray); - } - - [AutoData] - [Theory(DisplayName = "GIVEN enumerable WHEN ToTypedArray is invoked THEN array is returned")] - public void GivenEnumerable_WhenToTypedArrayIsInvoked_ThenArrayIsReturned(BitArray items) - { - // Arrange - var itemType = typeof(bool); - - // Act - var array = items.ToTypedArray(itemType); - - // Assert - Assert.Equal(items, array); - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.Tests.Common +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + + using global::AutoFixture.Xunit2; + + using Objectivity.AutoFixture.XUnit2.Core.Common; + + using Xunit; + + [Collection("EnumerableExtensions")] + [Trait("Category", "Common")] + public class EnumerableExtensionsTests + { + public static TheoryData InvalidCollectionTestData { get; } = new() + { + typeof(Tuple), + typeof(IEnumerable), + typeof(IEnumerable).GetGenericTypeDefinition(), + }; + + [Fact(DisplayName = "GIVEN uninitialized argument WHEN TryGetEnumerableSingleTypeArgument is invoked THEN exception is thrown")] + [SuppressMessage("ReSharper", "UnusedVariable", Justification = "This is good enougth to test the logic.")] + public void GivenUninitializedArgument_WhenTryGetEnumerableSingleTypeArgumentIsInvoked_ThenExceptionIsThrown() + { + // Arrange + Type enumerableType = null; + + // Act + object Act() => enumerableType.TryGetEnumerableSingleTypeArgument(out _); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal("type", exception.ParamName); + } + + [InlineData(typeof(int[]), typeof(int))] + [InlineData(typeof(List), typeof(int))] + [InlineData(typeof(Dictionary), typeof(KeyValuePair))] + [InlineData(typeof(IEnumerable), typeof(int))] + [Theory(DisplayName = "GIVEN valid collection WHEN TryGetEnumerableSingleTypeArgument is invoked THEN collection single type argument returned")] + public void GivenValidCollection_WhenTryGetEnumerableSingleTypeArgumentIsInvoked_ThenCollectionSingleTypeArgumentReturned(Type enumerableType, Type expectedType) + { + // Arrange + // Act + var isSuccessful = enumerableType.TryGetEnumerableSingleTypeArgument(out var itemType); + + // Assert + Assert.True(isSuccessful); + Assert.Equal(expectedType, itemType); + } + + [MemberData(nameof(InvalidCollectionTestData))] + [Theory(DisplayName = "GIVEN invalid collection WHEN TryGetEnumerableSingleTypeArgument is invoked THEN no argument returned")] + public void GivenInvalidCollection_WhenTryGetEnumerableSingleTypeArgumentIsInvoked_ThenNoArgumentReturned(Type enumerableType) + { + // Arrange + // Act + var isSuccessful = enumerableType.TryGetEnumerableSingleTypeArgument(out _); + + // Assert + Assert.False(isSuccessful); + } + + [InlineData(null, typeof(int), "items")] + [InlineData(new[] { 1 }, null, "itemType")] + [Theory(DisplayName = "GIVEN uninitialized argument WHEN ToTypedArray is invoked THEN exception is thrown")] + public void GivenUninitializedArgument_WhenToTypedArrayIsInvoked_ThenExceptionIsThrown( + IEnumerable items, + Type itemType, + string exceptionParamName) + { + // Arrange + // Act + object Act() => items.ToTypedArray(itemType); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal(exceptionParamName, exception.ParamName); + } + + [AutoData] + [Theory(DisplayName = "GIVEN typed enumerable WHEN ToTypedArray is invoked THEN array is returned")] + public void GivenTypedEnumerable_WhenToTypedArrayIsInvoked_ThenArrayIsReturned(int[] items) + { + // Arrange + var itemType = typeof(int); + + // Act + var array = items.ToTypedArray(itemType); + + // Assert + Assert.Equal(items, array); + Assert.True(array.GetType().IsArray); + } + + [AutoData] + [Theory(DisplayName = "GIVEN enumerable WHEN ToTypedArray is invoked THEN array is returned")] + public void GivenEnumerable_WhenToTypedArrayIsInvoked_ThenArrayIsReturned(BitArray items) + { + // Arrange + var itemType = typeof(bool); + + // Act + var array = items.ToTypedArray(itemType); + + // Assert + Assert.Equal(items, array); + } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs index 0f1b75c3..76e60ae6 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs @@ -14,6 +14,12 @@ [Trait("Category", "Requests")] public class ValuesRequestTests { + public static TheoryData NullArgumentConstructorTestData { get; } = new() + { + { null, null, "operandType" }, + { typeof(int), null, "values" }, + }; + public static TheoryData ComparisonTestData { get; } = new() { { typeof(int), new[] { 1 }, typeof(int), new[] { 1 }, true }, @@ -24,34 +30,18 @@ public class ValuesRequestTests { typeof(int), new[] { 1, 2 }, typeof(int), new[] { 2 }, false }, }; - [Fact(DisplayName = "GIVEN uninitialized type argument WHEN constructor is invoked THEN exception is thrown")] - public void GivenUninitializedTypeArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() + [MemberData(nameof(NullArgumentConstructorTestData))] + [Theory(DisplayName = "GIVEN uninitialized argument WHEN constructor is invoked THEN exception is thrown")] + public void GivenUninitializedArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown( + Type type, object[] values, string expectedParamName) { // Arrange - Type type = null; - object[] values = null; - - // Act - object Act() => new FixedValuesRequest(type, values); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("operandType", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN uninitialized values argument WHEN constructor is invoked THEN exception is thrown")] - public void GivenUninitializedValuesArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var type = typeof(int); - object[] values = null; - // Act object Act() => new FixedValuesRequest(type, values); // Assert var exception = Assert.Throws(Act); - Assert.Equal("values", exception.ParamName); + Assert.Equal(expectedParamName, exception.ParamName); } [Fact(DisplayName = "GIVEN empty values argument WHEN constructor is invoked THEN exception is thrown")] diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RandomExceptValuesGeneratorTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RandomExceptValuesGeneratorTests.cs index 49c87b90..bbb85c24 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RandomExceptValuesGeneratorTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RandomExceptValuesGeneratorTests.cs @@ -1,83 +1,75 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.Tests.SpecimenBuilders -{ - using System; - - using global::AutoFixture; - using global::AutoFixture.Kernel; - - using Moq; - - using Objectivity.AutoFixture.XUnit2.Core.Requests; - using Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders; - - using Xunit; - - [Collection("RandomExceptValuesGenerator")] - [Trait("Category", "SpecimenBuilders")] - public class RandomExceptValuesGeneratorTests - { - [Fact(DisplayName = "GIVEN uninitialized context WHEN Create is invoked THEN exception is thrown")] - public void GivenUninitializedContext_WhenCreateIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var builder = new RandomExceptValuesGenerator(); - - // Act - object Act() => builder.Create(new object(), null); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("context", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN uninitialized request WHEN Create is invoked THEN exception is thrown")] - public void GivenUninitializedRequest_WhenCreateIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var builder = new RandomExceptValuesGenerator(); - var context = new Mock(); - - // Act - object Act() => builder.Create(null, context.Object); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("request", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN excluded value and context which resolves to the same value WHEN Create is invoked THEN exception is thrown")] - public void GivenExcludedValueAndContextWhichResolvesToTheSameValue_WhenCreateIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var builder = new RandomExceptValuesGenerator(); - var context = new Mock(); - const int duplicateValue = 5; - context.Setup(x => x.Resolve(It.IsNotNull())).Returns(duplicateValue); - var request = new ExceptValuesRequest(duplicateValue.GetType(), duplicateValue); - - // Act - object Act() => builder.Create(request, context.Object); - - // Assert - var exception = Assert.Throws(Act); - Assert.NotNull(exception.Message); - Assert.NotEmpty(exception.Message); - } - - [Fact(DisplayName = "GIVEN unsupported request WHEN Create is invoked THEN NoSpecimen is returned")] - public void GivenUnsupportedRequest_WhenCreateIsInvoked_ThenNoSpecimenIsReturned() - { - // Arrange - var builder = new RandomExceptValuesGenerator(); - var context = new Mock(); - var request = typeof(string); - - // Act - var result = builder.Create(request, context.Object); - - // Assert - Assert.NotNull(result); - Assert.IsType(result); - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.Tests.SpecimenBuilders +{ + using System; + + using global::AutoFixture; + using global::AutoFixture.Kernel; + + using Moq; + + using Objectivity.AutoFixture.XUnit2.Core.Requests; + using Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders; + + using Xunit; + + [Collection("RandomExceptValuesGenerator")] + [Trait("Category", "SpecimenBuilders")] + public class RandomExceptValuesGeneratorTests + { + public static TheoryData NullArgumentCreateTestData { get; } = new() + { + { new object(), null, "context" }, + { null, new Mock().Object, "request" }, + }; + + [MemberData(nameof(NullArgumentCreateTestData))] + [Theory(DisplayName = "GIVEN uninitialized argument WHEN Create is invoked THEN exception is thrown")] + public void GivenUninitializedArgument_WhenCreateIsInvoked_ThenExceptionIsThrown(object request, ISpecimenContext context, string expectedParamName) + { + // Arrange + var builder = new RandomExceptValuesGenerator(); + + // Act + object Act() => builder.Create(request, context); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal(expectedParamName, exception.ParamName); + } + + [Fact(DisplayName = "GIVEN excluded value and context which resolves to the same value WHEN Create is invoked THEN exception is thrown")] + public void GivenExcludedValueAndContextWhichResolvesToTheSameValue_WhenCreateIsInvoked_ThenExceptionIsThrown() + { + // Arrange + var builder = new RandomExceptValuesGenerator(); + var context = new Mock(); + const int duplicateValue = 5; + context.Setup(x => x.Resolve(It.IsNotNull())).Returns(duplicateValue); + var request = new ExceptValuesRequest(duplicateValue.GetType(), duplicateValue); + + // Act + object Act() => builder.Create(request, context.Object); + + // Assert + var exception = Assert.Throws(Act); + Assert.NotNull(exception.Message); + Assert.NotEmpty(exception.Message); + } + + [Fact(DisplayName = "GIVEN unsupported request WHEN Create is invoked THEN NoSpecimen is returned")] + public void GivenUnsupportedRequest_WhenCreateIsInvoked_ThenNoSpecimenIsReturned() + { + // Arrange + var builder = new RandomExceptValuesGenerator(); + var context = new Mock(); + var request = typeof(string); + + // Act + var result = builder.Create(request, context.Object); + + // Assert + Assert.NotNull(result); + Assert.IsType(result); + } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RandomFixedValuesGeneratorTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RandomFixedValuesGeneratorTests.cs index e761e640..a06166b9 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RandomFixedValuesGeneratorTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RandomFixedValuesGeneratorTests.cs @@ -1,122 +1,114 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.Tests.SpecimenBuilders -{ - using System; - using System.Linq; - - using global::AutoFixture; - using global::AutoFixture.Kernel; - using global::AutoFixture.Xunit2; - using Moq; - - using Objectivity.AutoFixture.XUnit2.Core.Requests; - using Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders; - - using Xunit; - - [Collection("RandomFixedValuesGenerator")] - [Trait("Category", "SpecimenBuilders")] - public class RandomFixedValuesGeneratorTests - { - [Fact(DisplayName = "GIVEN uninitialized context WHEN Create is invoked THEN exception is thrown")] - public void GivenUninitializedContext_WhenCreateIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var builder = new RandomFixedValuesGenerator(); - - // Act - object Act() => builder.Create(new object(), null); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("context", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN uninitialized request WHEN Create is invoked THEN exception is thrown")] - public void GivenUninitializedRequest_WhenCreateIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var builder = new RandomFixedValuesGenerator(); - var context = new Mock(); - - // Act - object Act() => builder.Create(null, context.Object); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("request", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN unsupported request WHEN Create is invoked THEN NoSpecimen is returned")] - public void GivenUnsupportedRequest_WhenCreateIsInvoked_ThenNoSpecimenIsReturned() - { - // Arrange - var builder = new RandomFixedValuesGenerator(); - var context = new Mock(); - var request = typeof(string); - - // Act - var result = builder.Create(request, context.Object); - - // Assert - Assert.NotNull(result); - Assert.IsType(result); - } - - [Fact(DisplayName = "GIVEN request with many values WHEN Create is invoked many times THEN only defined values are returned")] - public void GivenRequestWithManyValues_WhenCreateIsInvokedManyTimes_ThenOnlyDefinedValuesAreReturned() - { - // Arrange - var builder = new RandomFixedValuesGenerator(); - var context = new Mock(); - var expectedValues = new object[] { 1, 2 }; - var request = new FixedValuesRequest(typeof(int), expectedValues); - - // Act - var result = Enumerable.Range(0, 10).Select((_) => (int)builder.Create(request, context.Object)).ToList(); - - // Assert - Assert.NotNull(result); - Assert.Equal(10, result.Count); - Assert.All(result, x => Assert.Contains(x, expectedValues)); - } - - [AutoData] - [Theory(DisplayName = "GIVEN request with single value WHEN Create is invoked many times THEN only repeated defined value is returned")] - internal void GivenRequestWithSingleValue_WhenCreateIsInvokedManyTimes_ThenOnlyRepeatedDefinedValueIsReturned( - RandomFixedValuesGenerator builder, - IFixture fixture) - { - // Arrange - var context = new Mock(); - var expectedValue = fixture.Create(); - var request = new FixedValuesRequest(typeof(int), expectedValue); - - // Act - var result = Enumerable.Range(0, 10).Select((_) => (int)builder.Create(request, context.Object)).ToList(); - - // Assert - Assert.NotNull(result); - Assert.Equal(10, result.Count); - Assert.All(result, x => Assert.Equal(expectedValue, x)); - } - - [AutoData] - [Theory(DisplayName = "GIVEN many requests WHEN Create is invoked per each request THEN different defined value are returned")] - internal void GivenManyRequests_WhenCreateIsInvokedPerEachRequest_ThenDifferentDefinedValueAreReturned( - RandomFixedValuesGenerator builder) - { - // Arrange - var context = new Mock(); - var setup = new[] { 1, 2 }.ToDictionary( - x => x, - x => new FixedValuesRequest(typeof(int), x)); - - // Act - var result = setup.Select(x => (int)builder.Create(x.Value, context.Object)).ToArray(); - - // Assert - Assert.Equal(setup.First().Key, result.First()); - Assert.Equal(setup.Last().Key, result.Last()); - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.Tests.SpecimenBuilders +{ + using System; + using System.Linq; + + using global::AutoFixture; + using global::AutoFixture.Kernel; + using global::AutoFixture.Xunit2; + using Moq; + + using Objectivity.AutoFixture.XUnit2.Core.Requests; + using Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders; + + using Xunit; + + [Collection("RandomFixedValuesGenerator")] + [Trait("Category", "SpecimenBuilders")] + public class RandomFixedValuesGeneratorTests + { + public static TheoryData NullArgumentCreateTestData { get; } = new() + { + { new object(), null, "context" }, + { null, new Mock().Object, "request" }, + }; + + [MemberData(nameof(NullArgumentCreateTestData))] + [Theory(DisplayName = "GIVEN uninitialized argument WHEN Create is invoked THEN exception is thrown")] + public void GivenUninitializedArgument_WhenCreateIsInvoked_ThenExceptionIsThrown(object request, ISpecimenContext context, string expectedParamName) + { + // Arrange + var builder = new RandomFixedValuesGenerator(); + + // Act + object Act() => builder.Create(request, context); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal(expectedParamName, exception.ParamName); + } + + [Fact(DisplayName = "GIVEN unsupported request WHEN Create is invoked THEN NoSpecimen is returned")] + public void GivenUnsupportedRequest_WhenCreateIsInvoked_ThenNoSpecimenIsReturned() + { + // Arrange + var builder = new RandomFixedValuesGenerator(); + var context = new Mock(); + var request = typeof(string); + + // Act + var result = builder.Create(request, context.Object); + + // Assert + Assert.NotNull(result); + Assert.IsType(result); + } + + [Fact(DisplayName = "GIVEN request with many values WHEN Create is invoked many times THEN only defined values are returned")] + public void GivenRequestWithManyValues_WhenCreateIsInvokedManyTimes_ThenOnlyDefinedValuesAreReturned() + { + // Arrange + var builder = new RandomFixedValuesGenerator(); + var context = new Mock(); + var expectedValues = new object[] { 1, 2 }; + var request = new FixedValuesRequest(typeof(int), expectedValues); + + // Act + var result = Enumerable.Range(0, 10).Select((_) => (int)builder.Create(request, context.Object)).ToList(); + + // Assert + Assert.NotNull(result); + Assert.Equal(10, result.Count); + Assert.All(result, x => Assert.Contains(x, expectedValues)); + } + + [AutoData] + [Theory(DisplayName = "GIVEN request with single value WHEN Create is invoked many times THEN only repeated defined value is returned")] + internal void GivenRequestWithSingleValue_WhenCreateIsInvokedManyTimes_ThenOnlyRepeatedDefinedValueIsReturned( + RandomFixedValuesGenerator builder, + IFixture fixture) + { + // Arrange + var context = new Mock(); + var expectedValue = fixture.Create(); + var request = new FixedValuesRequest(typeof(int), expectedValue); + + // Act + var result = Enumerable.Range(0, 10).Select((_) => (int)builder.Create(request, context.Object)).ToList(); + + // Assert + Assert.NotNull(result); + Assert.Equal(10, result.Count); + Assert.All(result, x => Assert.Equal(expectedValue, x)); + } + + [AutoData] + [Theory(DisplayName = "GIVEN many requests WHEN Create is invoked per each request THEN different defined value are returned")] + internal void GivenManyRequests_WhenCreateIsInvokedPerEachRequest_ThenDifferentDefinedValueAreReturned( + RandomFixedValuesGenerator builder) + { + // Arrange + var context = new Mock(); + var setup = new[] { 1, 2 }.ToDictionary( + x => x, + x => new FixedValuesRequest(typeof(int), x)); + + // Act + var result = setup.Select(x => (int)builder.Create(x.Value, context.Object)).ToArray(); + + // Assert + Assert.Equal(setup.First().Key, result.First()); + Assert.Equal(setup.Last().Key, result.Last()); + } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RequestFactoryRelayTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RequestFactoryRelayTests.cs index 76c23a0c..2df3fb8d 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RequestFactoryRelayTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/SpecimenBuilders/RequestFactoryRelayTests.cs @@ -1,206 +1,197 @@ -namespace Objectivity.AutoFixture.XUnit2.Core.Tests.SpecimenBuilders -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Linq; - using System.Reflection; - - using global::AutoFixture; - using global::AutoFixture.Kernel; - using global::AutoFixture.Xunit2; - - using Moq; - - using Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders; - - using Xunit; - - [Collection("RequestFactoryRelay")] - [Trait("Category", "SpecimenBuilders")] - public class RequestFactoryRelayTests - { - [Fact(DisplayName = "GIVEN uninitialized argument WHEN constructor is invoked THEN exception is thrown")] - public void GivenUninitializedArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() - { - // Arrange - // Act - static object Act() => new RequestFactoryRelay(null); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("requestFactory", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN empty argument WHEN Create is invoked THEN exception is thrown")] - public void GivenEmptyArgument_WhenCreateIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var factory = new Mock>(); - var builder = new RequestFactoryRelay(factory.Object); - - // Act - object Act() => builder.Create(new object(), null); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("context", exception.ParamName); - factory.VerifyNoOtherCalls(); - } - - [Fact(DisplayName = "GIVEN uninitialized request WHEN Create is invoked THEN exception is thrown")] - public void GivenUninitializedRequest_WhenCreateIsInvoked_ThenExceptionIsThrown() - { - // Arrange - var factory = new Mock>(); - var builder = new RequestFactoryRelay(factory.Object); - var context = new Mock(); - - // Act - object Act() => builder.Create(null, context.Object); - - // Assert - var exception = Assert.Throws(Act); - Assert.Equal("request", exception.ParamName); - } - - [Fact(DisplayName = "GIVEN unsupported request type WHEN create is invoked THEN NoSpecimen is returned")] - public void GivenUnsupportedRequestType_WhenCreateIsInvoked_ThenNoSpecimenIsReturned() - { - // Arrange - var factory = new Mock>(); - var builder = new RequestFactoryRelay(factory.Object); - var context = new Mock(); - var request = new object(); - - // Act - var result = builder.Create(request, context.Object); - - // Assert - Assert.IsType(result); - factory.VerifyNoOtherCalls(); - } - - [InlineAutoData(typeof(int))] - [InlineAutoData(typeof(int[]))] - [Theory(DisplayName = "GIVEN empty request WHEN create is invoked THEN NoSpecimen is returned")] - public void GivenEmptyRequest_WhenCreateIsInvoked_ThenNoSpecimenIsReturned( - Type requestType) - { - // Arrange - var factory = new Mock>(); - factory.Setup(x => x(It.IsAny())).Returns(null); - var builder = new RequestFactoryRelay(factory.Object); - var context = new Mock(); - var request = new Mock(); - request.SetupGet(x => x.ParameterType) - .Returns(requestType); - - // Act - var result = builder.Create(request.Object, context.Object); - - // Assert - Assert.IsType(result); - factory.Verify(x => x(It.IsAny()), Times.Once); - factory.VerifyNoOtherCalls(); - context.VerifyNoOtherCalls(); - } - - [InlineAutoData(typeof(int), typeof(int))] - [InlineAutoData(typeof(int?), typeof(int))] - [Theory(DisplayName = "GIVEN type request WHEN create is invoked THEN expected type is returned")] - public void GivenTypeRequest_WhenCreateIsInvoked_ThenExpectedTypeIsReturned( - Type requestType, - Type expectedType, - IFixture fixture) - { - // Arrange - var factory = new Mock>(); - factory.Setup(x => x(It.IsIn(expectedType))) - .Returns(x => x); - var builder = new RequestFactoryRelay(factory.Object); - var context = new Mock(); - context.Setup(x => x.Resolve(It.IsAny())) - .Returns((request) => new SpecimenContext(fixture).Resolve(request)); - var request = new Mock(); - request.SetupGet(x => x.ParameterType) - .Returns(requestType); - - // Act - var result = builder.Create(request.Object, context.Object); - - // Assert - Assert.NotNull(result); - Assert.IsType(expectedType, result); - factory.Verify(x => x(It.IsIn(expectedType)), Times.Once); - factory.VerifyNoOtherCalls(); - context.Verify(x => x.Resolve(It.IsAny()), Times.Once); - context.VerifyNoOtherCalls(); - } - - [AutoData] - [Theory(DisplayName = "GIVEN valid enumerable type request WHEN create is invoked resulting in unsupported type THEN NoSpecimen is returned")] - public void GivenValidEnumerableTypeRequest_WhenCreateIsInvokedResultingInUnsupportedType_ThenNoSpecimenIsReturned( - IFixture fixture) - { - // Arrange - var values = fixture.CreateMany(1).ToArray(); - var value = values.First(); - var factory = new Mock>(); - factory.Setup(x => x(It.IsAny())).Returns(value); - var builder = new RequestFactoryRelay(factory.Object); - var context = new Mock(); - context.Setup(x => x.Resolve(It.IsAny())).Returns(value); - var request = new Mock(); - request.SetupGet(x => x.ParameterType).Returns(values.GetType()); - - // Act - var result = builder.Create(request.Object, context.Object); - - // Assert - Assert.IsType(result); - factory.Verify(x => x(It.IsAny()), Times.Once); - factory.VerifyNoOtherCalls(); - context.Verify(x => x.Resolve(It.IsAny()), Times.Once); - context.VerifyNoOtherCalls(); - } - - [InlineAutoData(typeof(int[]), typeof(int[]))] - [InlineAutoData(typeof(int?[]), typeof(int?[]))] - [InlineAutoData(typeof(IEnumerable), typeof(int[]))] - [InlineAutoData(typeof(IEnumerable), typeof(int?[]))] - [InlineAutoData(typeof(Collection), typeof(Collection))] - [InlineAutoData(typeof(Collection), typeof(Collection))] - [Theory(DisplayName = "GIVEN valid enumerable type request WHEN create is invoked THEN expected collection is returned")] - public void GivenValidEnumerableTypeRequest_WhenCreateIsInvoked_ThenExpectedCollectionIsReturned( - Type requestType, - Type expectedType, - IFixture fixture) - { - // Arrange - var factory = new Mock>(); - factory.Setup(x => x(It.IsAny())) - .Returns(x => x); - var builder = new RequestFactoryRelay(factory.Object); - var context = new Mock(); - context.Setup(x => x.Resolve(It.IsAny())) - .Returns((request) => new SpecimenContext(fixture).Resolve(request)); - var request = new Mock(); - request.SetupGet(x => x.ParameterType) - .Returns(requestType); - - // Act - var result = builder.Create(request.Object, context.Object); - - // Assert - Assert.NotNull(result); - Assert.IsType(expectedType, result); - Assert.NotEmpty(((IEnumerable)result).Cast()); - factory.Verify(x => x(It.IsAny()), Times.Once); - factory.VerifyNoOtherCalls(); - context.Verify(x => x.Resolve(It.IsAny()), Times.Once); - context.VerifyNoOtherCalls(); - } - } -} +namespace Objectivity.AutoFixture.XUnit2.Core.Tests.SpecimenBuilders +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq; + using System.Reflection; + + using global::AutoFixture; + using global::AutoFixture.Kernel; + using global::AutoFixture.Xunit2; + + using Moq; + + using Objectivity.AutoFixture.XUnit2.Core.SpecimenBuilders; + + using Xunit; + + [Collection("RequestFactoryRelay")] + [Trait("Category", "SpecimenBuilders")] + public class RequestFactoryRelayTests + { + public static TheoryData NullArgumentCreateTestData { get; } = new() + { + { new object(), null, "context" }, + { null, new Mock().Object, "request" }, + }; + + [Fact(DisplayName = "GIVEN uninitialized argument WHEN constructor is invoked THEN exception is thrown")] + public void GivenUninitializedArgument_WhenConstructorIsInvoked_ThenExceptionIsThrown() + { + // Arrange + // Act + static object Act() => new RequestFactoryRelay(null); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal("requestFactory", exception.ParamName); + } + + [MemberData(nameof(NullArgumentCreateTestData))] + [Theory(DisplayName = "GIVEN uninitialized argument WHEN Create is invoked THEN exception is thrown")] + public void GivenUninitializedArgument_WhenCreateIsInvoked_ThenExceptionIsThrown(object request, ISpecimenContext context, string expectedParamName) + { + // Arrange + var factory = new Mock>(); + var builder = new RequestFactoryRelay(factory.Object); + + // Act + object Act() => builder.Create(request, context); + + // Assert + var exception = Assert.Throws(Act); + Assert.Equal(expectedParamName, exception.ParamName); + factory.VerifyNoOtherCalls(); + } + + [Fact(DisplayName = "GIVEN unsupported request type WHEN create is invoked THEN NoSpecimen is returned")] + public void GivenUnsupportedRequestType_WhenCreateIsInvoked_ThenNoSpecimenIsReturned() + { + // Arrange + var factory = new Mock>(); + var builder = new RequestFactoryRelay(factory.Object); + var context = new Mock(); + var request = new object(); + + // Act + var result = builder.Create(request, context.Object); + + // Assert + Assert.IsType(result); + factory.VerifyNoOtherCalls(); + } + + [InlineAutoData(typeof(int))] + [InlineAutoData(typeof(int[]))] + [Theory(DisplayName = "GIVEN empty request WHEN create is invoked THEN NoSpecimen is returned")] + public void GivenEmptyRequest_WhenCreateIsInvoked_ThenNoSpecimenIsReturned( + Type requestType) + { + // Arrange + var factory = new Mock>(); + factory.Setup(x => x(It.IsAny())).Returns(null); + var builder = new RequestFactoryRelay(factory.Object); + var context = new Mock(); + var request = new Mock(); + request.SetupGet(x => x.ParameterType) + .Returns(requestType); + + // Act + var result = builder.Create(request.Object, context.Object); + + // Assert + Assert.IsType(result); + factory.Verify(x => x(It.IsAny()), Times.Once); + factory.VerifyNoOtherCalls(); + context.VerifyNoOtherCalls(); + } + + [InlineAutoData(typeof(int), typeof(int))] + [InlineAutoData(typeof(int?), typeof(int))] + [Theory(DisplayName = "GIVEN type request WHEN create is invoked THEN expected type is returned")] + public void GivenTypeRequest_WhenCreateIsInvoked_ThenExpectedTypeIsReturned( + Type requestType, + Type expectedType, + IFixture fixture) + { + // Arrange + var factory = new Mock>(); + factory.Setup(x => x(It.IsIn(expectedType))) + .Returns(x => x); + var builder = new RequestFactoryRelay(factory.Object); + var context = new Mock(); + context.Setup(x => x.Resolve(It.IsAny())) + .Returns((request) => new SpecimenContext(fixture).Resolve(request)); + var request = new Mock(); + request.SetupGet(x => x.ParameterType) + .Returns(requestType); + + // Act + var result = builder.Create(request.Object, context.Object); + + // Assert + Assert.NotNull(result); + Assert.IsType(expectedType, result); + factory.Verify(x => x(It.IsIn(expectedType)), Times.Once); + factory.VerifyNoOtherCalls(); + context.Verify(x => x.Resolve(It.IsAny()), Times.Once); + context.VerifyNoOtherCalls(); + } + + [AutoData] + [Theory(DisplayName = "GIVEN valid enumerable type request WHEN create is invoked resulting in unsupported type THEN NoSpecimen is returned")] + public void GivenValidEnumerableTypeRequest_WhenCreateIsInvokedResultingInUnsupportedType_ThenNoSpecimenIsReturned( + IFixture fixture) + { + // Arrange + var values = fixture.CreateMany(1).ToArray(); + var value = values.First(); + var factory = new Mock>(); + factory.Setup(x => x(It.IsAny())).Returns(value); + var builder = new RequestFactoryRelay(factory.Object); + var context = new Mock(); + context.Setup(x => x.Resolve(It.IsAny())).Returns(value); + var request = new Mock(); + request.SetupGet(x => x.ParameterType).Returns(values.GetType()); + + // Act + var result = builder.Create(request.Object, context.Object); + + // Assert + Assert.IsType(result); + factory.Verify(x => x(It.IsAny()), Times.Once); + factory.VerifyNoOtherCalls(); + context.Verify(x => x.Resolve(It.IsAny()), Times.Once); + context.VerifyNoOtherCalls(); + } + + [InlineAutoData(typeof(int[]), typeof(int[]))] + [InlineAutoData(typeof(int?[]), typeof(int?[]))] + [InlineAutoData(typeof(IEnumerable), typeof(int[]))] + [InlineAutoData(typeof(IEnumerable), typeof(int?[]))] + [InlineAutoData(typeof(Collection), typeof(Collection))] + [InlineAutoData(typeof(Collection), typeof(Collection))] + [Theory(DisplayName = "GIVEN valid enumerable type request WHEN create is invoked THEN expected collection is returned")] + public void GivenValidEnumerableTypeRequest_WhenCreateIsInvoked_ThenExpectedCollectionIsReturned( + Type requestType, + Type expectedType, + IFixture fixture) + { + // Arrange + var factory = new Mock>(); + factory.Setup(x => x(It.IsAny())) + .Returns(x => x); + var builder = new RequestFactoryRelay(factory.Object); + var context = new Mock(); + context.Setup(x => x.Resolve(It.IsAny())) + .Returns((request) => new SpecimenContext(fixture).Resolve(request)); + var request = new Mock(); + request.SetupGet(x => x.ParameterType) + .Returns(requestType); + + // Act + var result = builder.Create(request.Object, context.Object); + + // Assert + Assert.NotNull(result); + Assert.IsType(expectedType, result); + Assert.NotEmpty(((IEnumerable)result).Cast()); + factory.Verify(x => x(It.IsAny()), Times.Once); + factory.VerifyNoOtherCalls(); + context.Verify(x => x.Resolve(It.IsAny()), Times.Once); + context.VerifyNoOtherCalls(); + } + } +} From 7345c2a5ac679e658717eb15c0420aca3afb1f72 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Wed, 22 Apr 2026 08:01:35 +0200 Subject: [PATCH 2/3] chore(backlog): Fix the acceptance criteria wording --- .../task-17 - Consolidate-duplicate-Facts-into-Theories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md b/.backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md index 0358623b..b53ad5aa 100644 --- a/.backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md +++ b/.backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md @@ -14,6 +14,6 @@ priority: low ## Acceptance Criteria -- [x] #1 Identify all [Fact] methods across the all test projects that test the same behavior with different inputs +- [x] #1 Identify all [Fact] methods across all test projects that test the same behavior with different inputs - [x] #2 Replace duplicate Facts with a single [Theory] using [InlineData] or [MemberData]/[TheoryData] as appropriate From 0048fbcddc903a1139deb962534286578bbc03b1 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Wed, 22 Apr 2026 08:02:43 +0200 Subject: [PATCH 3/3] fix(test): Keep non-target arguments valid in guard-clause data --- .../Requests/ValuesRequestTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs index 76e60ae6..96cc2587 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Requests/ValuesRequestTests.cs @@ -16,7 +16,7 @@ public class ValuesRequestTests { public static TheoryData NullArgumentConstructorTestData { get; } = new() { - { null, null, "operandType" }, + { null, new object[] { 1 }, "operandType" }, { typeof(int), null, "values" }, };