From b8366afe08a7009c49f91eb63b3fa3a1d869a66d Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Wed, 27 Sep 2023 12:49:12 +0200 Subject: [PATCH 01/10] Improve tests by making them immune to mutations --- .../Attributes/AutoMockDataAttributeTests.cs | 17 ++++++++++++----- .../InlineAutoMockDataAttributeTests.cs | 13 ++++++++----- .../MemberAutoMockDataAttributeTests.cs | 11 ++++++++--- .../IFakeObjectUnderTest.cs | 7 +++++++ .../Attributes/AutoMockDataAttributeTests.cs | 17 ++++++++++++----- .../InlineAutoMockDataAttributeTests.cs | 13 ++++++++----- .../MemberAutoMockDataAttributeTests.cs | 11 ++++++++--- .../IFakeObjectUnderTest.cs | 7 +++++++ .../Attributes/AutoMockDataAttributeTests.cs | 17 ++++++++++++----- .../InlineAutoMockDataAttributeTests.cs | 13 ++++++++----- .../MemberAutoMockDataAttributeTests.cs | 11 ++++++++--- .../IFakeObjectUnderTest.cs | 7 +++++++ 12 files changed, 105 insertions(+), 39 deletions(-) create mode 100644 src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/IFakeObjectUnderTest.cs create mode 100644 src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/IFakeObjectUnderTest.cs create mode 100644 src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/IFakeObjectUnderTest.cs diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/AutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/AutoMockDataAttributeTests.cs index b91d32aa..d536db9c 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/AutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/AutoMockDataAttributeTests.cs @@ -1,6 +1,5 @@ namespace Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests.Attributes { - using System; using System.Collections.Generic; using System.Reflection; @@ -80,16 +79,24 @@ public void WhenGetDataIsInvoked_ThenFixtureIsConfiguredAndDataReturned(bool ign } [AutoMockData] - [Theory(DisplayName = "GIVEN test method has some parameters WHEN test run THEN parameters are generated")] - public void GivenTestMethodHasSomeParameters_WhenTestRun_ThenParametersAreGenerated(int value, IDisposable disposable) + [Theory(DisplayName = "GIVEN test method has some value parameters WHEN test run THEN parameters are generated")] + public void GivenTestMethodHasSomeValueParameters_WhenTestRun_ThenParametersAreGenerated(int value) { // Arrange // Act // Assert value.Should().NotBe(default); + } - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().Contain("Proxy", "that way we know it was mocked."); + [AutoMockData] + [Theory(DisplayName = "GIVEN test method has some object parameters WHEN test run THEN parameters are generated")] + public void GivenTestMethodHasSomeObjectParameters_WhenTestRun_ThenParametersAreGenerated(IFakeObjectUnderTest value) + { + // Arrange + // Act + // Assert + value.Should().NotBeNull(); + value.StringProperty.Should().NotBeNullOrEmpty(); } protected void MethodUnderTest() diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/InlineAutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/InlineAutoMockDataAttributeTests.cs index 78d2132f..03f96acb 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/InlineAutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/InlineAutoMockDataAttributeTests.cs @@ -1,6 +1,5 @@ namespace Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests.Attributes { - using System; using System.Collections.Generic; using System.Reflection; @@ -114,15 +113,19 @@ public void WhenGetDataIsInvoked_ThenFixtureIsConfiguredAndDataReturned(bool ign [InlineAutoMockData(100)] [Theory(DisplayName = "GIVEN test method has some inline parameters WHEN test run THEN parameters are generated")] - public void GivenTestMethodHasSomeInlineParameters_WhenTestRun_ThenParametersAreGenerated(int value, IDisposable disposable) + public void GivenTestMethodHasSomeInlineParameters_WhenTestRun_ThenParametersAreGenerated( + int firstValueInstance, + int secondValueInstance, + IFakeObjectUnderTest objectInstance) { // Arrange // Act // Assert - value.Should().Be(100); + firstValueInstance.Should().Be(100); + secondValueInstance.Should().NotBe(default); - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().Contain("Proxy", "that way we know it was mocked."); + objectInstance.Should().NotBeNull(); + objectInstance.StringProperty.Should().NotBeNullOrEmpty(); } protected void MethodUnderTest() diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/MemberAutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/MemberAutoMockDataAttributeTests.cs index d06a7d8a..c5414ffa 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/MemberAutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/Attributes/MemberAutoMockDataAttributeTests.cs @@ -164,7 +164,12 @@ public void GivenUninitializedMemberName_WhenConstructorIsInvoked_ThenExceptionI [Theory(DisplayName = "GIVEN test method has some member generated parameters WHEN test run THEN parameters are provided")] [MemberAutoMockData(nameof(TestData))] - public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenParametersAreProvided(int first, int second, int third, int fourth, IDisposable disposable) + public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenParametersAreProvided( + int first, + int second, + int third, + int fourth, + IFakeObjectUnderTest objectInstance) { // Arrange var testData = TestData.ToList(); @@ -176,8 +181,8 @@ public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenPara third.Should().BeOneOf((int)testData[0][2], (int)testData[1][2], (int)testData[2][2]); fourth.Should().NotBe(default); - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().Contain("Proxy", "that way we know it was mocked."); + objectInstance.Should().NotBeNull(); + objectInstance.StringProperty.Should().NotBeNullOrEmpty(); } } } diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/IFakeObjectUnderTest.cs b/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/IFakeObjectUnderTest.cs new file mode 100644 index 00000000..bd778081 --- /dev/null +++ b/src/Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests/IFakeObjectUnderTest.cs @@ -0,0 +1,7 @@ +namespace Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Tests +{ + public interface IFakeObjectUnderTest + { + public string StringProperty { get; set; } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/AutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/AutoMockDataAttributeTests.cs index f430187b..968b3746 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/AutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/AutoMockDataAttributeTests.cs @@ -1,6 +1,5 @@ namespace Objectivity.AutoFixture.XUnit2.AutoMoq.Tests.Attributes { - using System; using System.Collections.Generic; using System.Reflection; @@ -80,16 +79,24 @@ public void WhenGetDataIsInvoked_ThenFixtureIsConfiguredAndDataReturned(bool ign } [AutoMockData] - [Theory(DisplayName = "GIVEN test method has some parameters WHEN test run THEN parameters are generated")] - public void GivenTestMethodHasSomeParameters_WhenTestRun_ThenParametersAreGenerated(int value, IDisposable disposable) + [Theory(DisplayName = "GIVEN test method has some value parameters WHEN test run THEN parameters are generated")] + public void GivenTestMethodHasSomeValueParameters_WhenTestRun_ThenParametersAreGenerated(int value) { // Arrange // Act // Assert value.Should().NotBe(default); + } - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().StartWith("IDisposableProxy", "that way we know it was mocked."); + [AutoMockData] + [Theory(DisplayName = "GIVEN test method has some object parameters WHEN test run THEN parameters are generated")] + public void GivenTestMethodHasSomeObjectParameters_WhenTestRun_ThenParametersAreGenerated(IFakeObjectUnderTest value) + { + // Arrange + // Act + // Assert + value.Should().NotBeNull(); + value.StringProperty.Should().NotBeNullOrEmpty(); } protected void MethodUnderTest() diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/InlineAutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/InlineAutoMockDataAttributeTests.cs index d5bfc249..0b690c6c 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/InlineAutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/InlineAutoMockDataAttributeTests.cs @@ -1,6 +1,5 @@ namespace Objectivity.AutoFixture.XUnit2.AutoMoq.Tests.Attributes { - using System; using System.Collections.Generic; using System.Reflection; @@ -114,15 +113,19 @@ public void WhenGetDataIsInvoked_ThenFixtureIsConfiguredAndDataReturned(bool ign [InlineAutoMockData(100)] [Theory(DisplayName = "GIVEN test method has some inline parameters WHEN test run THEN parameters are generated")] - public void GivenTestMethodHasSomeInlineParameters_WhenTestRun_ThenParametersAreGenerated(int value, IDisposable disposable) + public void GivenTestMethodHasSomeInlineParameters_WhenTestRun_ThenParametersAreGenerated( + int firstValueInstance, + int secondValueInstance, + IFakeObjectUnderTest objectInstance) { // Arrange // Act // Assert - value.Should().Be(100); + firstValueInstance.Should().Be(100); + secondValueInstance.Should().NotBe(default); - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().StartWith("IDisposableProxy", "that way we know it was mocked."); + objectInstance.Should().NotBeNull(); + objectInstance.StringProperty.Should().NotBeNullOrEmpty(); } protected void MethodUnderTest() diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/MemberAutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/MemberAutoMockDataAttributeTests.cs index bb444740..96084437 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/MemberAutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/Attributes/MemberAutoMockDataAttributeTests.cs @@ -164,7 +164,12 @@ public void GivenUninitializedMemberName_WhenConstructorIsInvoked_ThenExceptionI [Theory(DisplayName = "GIVEN test method has some member generated parameters WHEN test run THEN parameters are provided")] [MemberAutoMockData(nameof(TestData))] - public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenParametersAreProvided(int first, int second, int third, int fourth, IDisposable disposable) + public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenParametersAreProvided( + int first, + int second, + int third, + int fourth, + IFakeObjectUnderTest objectInstance) { // Arrange var testData = TestData.ToList(); @@ -176,8 +181,8 @@ public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenPara third.Should().BeOneOf((int)testData[0][2], (int)testData[1][2], (int)testData[2][2]); fourth.Should().NotBe(default); - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().StartWith("IDisposableProxy", "that way we know it was mocked."); + objectInstance.Should().NotBeNull(); + objectInstance.StringProperty.Should().NotBeNullOrEmpty(); } } } diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/IFakeObjectUnderTest.cs b/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/IFakeObjectUnderTest.cs new file mode 100644 index 00000000..ecada34d --- /dev/null +++ b/src/Objectivity.AutoFixture.XUnit2.AutoMoq.Tests/IFakeObjectUnderTest.cs @@ -0,0 +1,7 @@ +namespace Objectivity.AutoFixture.XUnit2.AutoMoq.Tests +{ + public interface IFakeObjectUnderTest + { + public string StringProperty { get; set; } + } +} diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/AutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/AutoMockDataAttributeTests.cs index a4bed765..1112f003 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/AutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/AutoMockDataAttributeTests.cs @@ -1,6 +1,5 @@ namespace Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests.Attributes { - using System; using System.Collections.Generic; using System.Reflection; @@ -79,16 +78,24 @@ public void WhenGetDataIsInvoked_ThenFixtureIsConfiguredAndDataReturned(bool ign } [AutoMockData] - [Theory(DisplayName = "GIVEN test method has some parameters WHEN test run THEN parameters are generated")] - public void GivenTestMethodHasSomeParameters_WhenTestRun_ThenParametersAreGenerated(int value, IDisposable disposable) + [Theory(DisplayName = "GIVEN test method has some value parameters WHEN test run THEN parameters are generated")] + public void GivenTestMethodHasSomeValueParameters_WhenTestRun_ThenParametersAreGenerated(int value) { // Arrange // Act // Assert value.Should().NotBe(default); + } - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().StartWith("ObjectProxy", "that way we know it was mocked."); + [AutoMockData] + [Theory(DisplayName = "GIVEN test method has some object parameters WHEN test run THEN parameters are generated")] + public void GivenTestMethodHasSomeObjectParameters_WhenTestRun_ThenParametersAreGenerated(IFakeObjectUnderTest value) + { + // Arrange + // Act + // Assert + value.Should().NotBeNull(); + value.StringProperty.Should().NotBeNullOrEmpty(); } protected void MethodUnderTest() diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/InlineAutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/InlineAutoMockDataAttributeTests.cs index 81c19dbc..c1dfac7d 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/InlineAutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/InlineAutoMockDataAttributeTests.cs @@ -1,6 +1,5 @@ namespace Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests.Attributes { - using System; using System.Collections.Generic; using System.Reflection; @@ -113,15 +112,19 @@ public void WhenGetDataIsInvoked_ThenFixtureIsConfiguredAndDataReturned(bool ign [InlineAutoMockData(100)] [Theory(DisplayName = "GIVEN test method has some inline parameters WHEN test run THEN parameters are generated")] - public void GivenTestMethodHasSomeInlineParameters_WhenTestRun_ThenParametersAreGenerated(int value, IDisposable disposable) + public void GivenTestMethodHasSomeInlineParameters_WhenTestRun_ThenParametersAreGenerated( + int firstValueInstance, + int secondValueInstance, + IFakeObjectUnderTest objectInstance) { // Arrange // Act // Assert - value.Should().Be(100); + firstValueInstance.Should().Be(100); + secondValueInstance.Should().NotBe(default); - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().StartWith("ObjectProxy", "that way we know it was mocked."); + objectInstance.Should().NotBeNull(); + objectInstance.StringProperty.Should().NotBeNullOrEmpty(); } protected void MethodUnderTest() diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/MemberAutoMockDataAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/MemberAutoMockDataAttributeTests.cs index 942356d2..4b2a09da 100644 --- a/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/MemberAutoMockDataAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/Attributes/MemberAutoMockDataAttributeTests.cs @@ -163,7 +163,12 @@ public void GivenUninitializedMemberName_WhenConstructorIsInvoked_ThenExceptionI [Theory(DisplayName = "GIVEN test method has some member generated parameters WHEN test run THEN parameters are provided")] [MemberAutoMockData(nameof(TestData))] - public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenParametersAreProvided(int first, int second, int third, int fourth, IDisposable disposable) + public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenParametersAreProvided( + int first, + int second, + int third, + int fourth, + IFakeObjectUnderTest objectInstance) { // Arrange var testData = TestData.ToList(); @@ -175,8 +180,8 @@ public void GivenTestMethodHasSomeMemberGeneratedParameters_WhenTestRun_ThenPara third.Should().BeOneOf((int)testData[0][2], (int)testData[1][2], (int)testData[2][2]); fourth.Should().NotBe(default); - disposable.Should().NotBeNull(); - disposable.GetType().Name.Should().StartWith("ObjectProxy", "that way we know it was mocked."); + objectInstance.Should().NotBeNull(); + objectInstance.StringProperty.Should().NotBeNullOrEmpty(); } } } diff --git a/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/IFakeObjectUnderTest.cs b/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/IFakeObjectUnderTest.cs new file mode 100644 index 00000000..6189c5e1 --- /dev/null +++ b/src/Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests/IFakeObjectUnderTest.cs @@ -0,0 +1,7 @@ +namespace Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Tests +{ + public interface IFakeObjectUnderTest + { + public string StringProperty { get; set; } + } +} From f2feecb5144860de5d42f72dfb1ed246599820ab Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Wed, 27 Sep 2023 20:14:46 +0200 Subject: [PATCH 02/10] Improve tests by making them immune to mutations --- .../Attributes/CustomizeWithAttributeTests.cs | 3 +- .../MemberAutoDataItemConverterTests.cs | 52 +++++++++++++++---- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs index aea2712b..a1d84046 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/CustomizeWithAttributeTests.cs @@ -100,7 +100,8 @@ public void GivenUnsupportedType_WhenConstructorIsInvoked_ThenExceptionIsThrown( // Act // Assert - Assert.Throws(() => new CustomizeWithAttribute(customizationType)); + var exception = Assert.Throws(() => new CustomizeWithAttribute(customizationType)); + exception.Message.Should().NotBeNullOrEmpty().And.Contain(nameof(ICustomization)); } [Fact(DisplayName = "GIVEN CustomizeWith attribute with IncludeParameterType set WHEN GetCustomization is invoked THEN customization with expected type is returned")] diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/MemberData/MemberAutoDataItemConverterTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/MemberData/MemberAutoDataItemConverterTests.cs index 5d163e77..26f53cd6 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/MemberData/MemberAutoDataItemConverterTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/MemberData/MemberAutoDataItemConverterTests.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Linq; using System.Reflection; using FluentAssertions; @@ -20,12 +21,12 @@ [Trait("Category", "MemberData")] public class MemberAutoDataItemConverterTests { + private static readonly Type MemberType = typeof(MemberAutoDataItemConverterTests); + private static readonly MethodInfo TestMethod = MemberType.GetMethod(nameof(MethodUnderTest), BindingFlags.Instance | BindingFlags.NonPublic); private readonly Fixture fixture = new(); private readonly Mock dataAttributeProvider = new(); private readonly Mock dataAttribute = new(); private readonly IDataItemConverter converter; - private readonly Type memberType = typeof(MemberAutoDataItemConverterTests); - private readonly MethodInfo testMethod; private readonly string memberName; public MemberAutoDataItemConverterTests() @@ -34,10 +35,37 @@ public MemberAutoDataItemConverterTests() this.dataAttributeProvider.Setup(p => p.GetAttribute(this.fixture, It.IsAny())).Returns(this.dataAttribute.Object); this.dataAttribute.Setup(a => a.GetData(It.IsAny())).Returns(data); this.converter = new MemberAutoDataItemConverter(this.fixture, this.dataAttributeProvider.Object); - this.testMethod = this.memberType.GetMethod(nameof(this.MethodUnderTest), BindingFlags.Instance | BindingFlags.NonPublic); this.memberName = this.fixture.Create(); } + public static IEnumerable MemberTypeTestData { get; } = new[] + { + new object[] { MemberType, MemberType.Name }, + new object[] { typeof(string), nameof(String) }, + new object[] { null, MemberType.Name }, + }; + + [Fact(DisplayName = "GIVEN provider with no data attribute WHEN Convert is invoked THEN Null is returned")] + public void GivenProviderWithNoDataAttribute_WhenConvertIsInvoked_ThenNullReturned() + { + // Arrange + var noData = Enumerable.Empty(); + var noDataAttribute = new Mock(); + noDataAttribute.Setup(a => a.GetData(It.IsAny())).Returns(noData); + var noDataProvider = new Mock(); + noDataProvider.Setup(x => x.GetAttribute(It.IsAny(), It.IsNotNull())).Returns(noDataAttribute.Object); + var noDataConverter = new MemberAutoDataItemConverter(this.fixture, noDataProvider.Object); + var item = this.fixture.Create(); + + // Act + var data = noDataConverter.Convert(TestMethod, item, this.memberName, MemberType); + + // Assert + data.Should().BeNull(); + noDataProvider.VerifyAll(); + noDataAttribute.VerifyAll(); + } + [Fact(DisplayName = "GIVEN valid parameters WHEN Convert is invoked THEN appropriate code is invoked and data is returned")] public void GivenValidParameters_WhenConvertIsInvoked_ThenAppropriateCodeIsInvokedAndDataIsReturned() { @@ -45,7 +73,7 @@ public void GivenValidParameters_WhenConvertIsInvoked_ThenAppropriateCodeIsInvok var item = this.fixture.Create(); // Act - var data = this.converter.Convert(this.testMethod, item, this.memberName, this.memberType); + var data = this.converter.Convert(TestMethod, item, this.memberName, MemberType); // Assert data.Should().NotBeNull(); @@ -53,28 +81,32 @@ public void GivenValidParameters_WhenConvertIsInvoked_ThenAppropriateCodeIsInvok this.dataAttribute.VerifyAll(); } - [Fact(DisplayName = "GIVEN uninitialized item WHEN Convert is invoked THEN exception is thrown")] + [Fact(DisplayName = "GIVEN uninitialized item WHEN Convert is invoked THEN Null is returned")] public void GivenUninitializedItem_WhenConvertInvoked_ThenNullReturned() { // Arrange const object item = null; // Act - var data = this.converter.Convert(this.testMethod, item, this.memberName, this.memberType); + var data = this.converter.Convert(TestMethod, item, this.memberName, MemberType); // Assert data.Should().BeNull(); } - [Fact(DisplayName = "GIVEN item of unexpected type WHEN Convert is invoked THEN exception is thrown")] - public void GivenItemOfUnexpectedType_WhenConvertIsInvoked_ThenExceptionIsThrown() + [Theory(DisplayName = "GIVEN item of unexpected type WHEN Convert is invoked THEN exception is thrown")] + [MemberData(nameof(MemberTypeTestData))] + public void GivenItemOfUnexpectedType_WhenConvertIsInvoked_ThenExceptionIsThrown( + Type memberType, + string expectedTypeName) { // Arrange var item = this.fixture.Create(); // Act // Assert - Assert.Throws(() => this.converter.Convert(this.testMethod, item, this.memberName, this.memberType)); + var exception = Assert.Throws(() => this.converter.Convert(TestMethod, item, this.memberName, memberType)); + exception.Message.Should().NotBeNullOrEmpty().And.Contain(this.memberName).And.Contain(expectedTypeName); } [Fact(DisplayName = "GIVEN uninitialized test method WHEN Convert is invoked THEN exception is thrown")] @@ -86,7 +118,7 @@ public void GivenUninitializedTestMethod_WhenConvertIsInvoked_ThenExceptionIsThr // Act // Assert - Assert.Throws(() => this.converter.Convert(method, item, this.memberName, this.memberType)); + Assert.Throws(() => this.converter.Convert(method, item, this.memberName, MemberType)); } protected void MethodUnderTest() From 9b359addd57d059e063ceb4bd033269ddd57f1d7 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Thu, 28 Sep 2023 07:41:43 +0200 Subject: [PATCH 03/10] Improve tests by making them immune to mutations --- .../AutoDataAdapterAttributeTests.cs | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs index 1ea67e2f..00fc2fc3 100644 --- a/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs +++ b/src/Objectivity.AutoFixture.XUnit2.Core.Tests/Attributes/AutoDataAdapterAttributeTests.cs @@ -8,6 +8,7 @@ using FluentAssertions; using global::AutoFixture; + using global::AutoFixture.Kernel; using global::AutoFixture.Xunit2; using Objectivity.AutoFixture.XUnit2.Core.Attributes; @@ -59,13 +60,14 @@ public void GivenTestDataWithInstance_WhenGetDataCalled_ThenAutoDataGenerationSk IFixture fixture = new Fixture(); var attribute = new AutoDataAdapterAttribute(fixture, SpecificTestClass.Create()); var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithAbstractTestClass), BindingFlags.Instance | BindingFlags.NonPublic); + var expectedNumberOfParameters = methodInfo.GetParameters().Length; // Act var data = attribute.GetData(methodInfo).ToArray(); // Assert data.Should().HaveCount(1) - .And.Subject.First().Should().HaveCount(methodInfo.GetParameters().Length) + .And.Subject.First().Should().HaveCount(expectedNumberOfParameters) .And.NotContainNulls() .And.Subject.Skip(1).Should().AllBeEquivalentTo(data.First().Last()); } @@ -85,11 +87,47 @@ public void GivenEmptyTestData_WhenGetDataCalled_ThenAutoDataGenerationSkipped() data.Should().Throw(); } - protected string TestMethodWithAbstractTestClass(SpecificTestClass instance, [Frozen] string text, string message) + [Fact(DisplayName = "GIVEN test method with Frozen customization after others WHEN GetData called THEN ensure parameter is frozen on the end")] + public void GivenTestMethodWithFrozenCustomizationAfterOthers_WhenGetDataCalled_ThenEnsureParameterIsFrozenOnTheEnd() + { + // Arrange + IFixture fixture = new Fixture(); + var attribute = new AutoDataAdapterAttribute(fixture, null); + var methodInfo = typeof(AutoDataAdapterAttributeTests).GetMethod(nameof(this.TestMethodWithFrozenCustomizationBeforeOthers), BindingFlags.Instance | BindingFlags.NonPublic); + var expectedNumberOfParameters = methodInfo.GetParameters().Length; + + // Act + var data = attribute.GetData(methodInfo).ToArray(); + + // Assert + data.Should().HaveCount(1) + .And.Subject.First().Should().HaveCount(expectedNumberOfParameters) + .And.NotContainNulls() + .And.Subject.Skip(1).Should().AllSatisfy((x) => + { + var parameters = data.First(); + x.Should().Be(parameters.Last()) + .And.NotBe(parameters.First()) + .And.Subject.As().Should().Contain(StopStringCustomization.Value); + }); + } + + protected string TestMethodWithAbstractTestClass( + SpecificTestClass instance, + [Frozen] string text, + string message) { return $"{instance}: {text}, {message}"; } + protected string TestMethodWithFrozenCustomizationBeforeOthers( + string first, + [Frozen][StopString] string second, + string third) + { + return $"{first}: {second}, {third}"; + } + [SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty", Justification = "Test class")] public abstract class AbstractTestClass { @@ -106,5 +144,22 @@ public static AbstractTestClass Create() return new SpecificTestClass(); } } + + protected sealed class StopStringAttribute : CustomizeWithAttribute + { + } + + protected class StopStringCustomization : ICustomization + { + public const string Value = "STOP"; + + public void Customize(IFixture fixture) + { + fixture.Customizations.Add( + new FilteringSpecimenBuilder( + new FixedBuilder(Value), + new ExactTypeSpecification(Value.GetType()))); + } + } } } From d541ff47218d8c1d6512389800aa95f7fef9e916 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Thu, 28 Sep 2023 10:44:32 +0200 Subject: [PATCH 04/10] Mutations testing pipeline --- .github/workflows/test-mutations.yml | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/test-mutations.yml diff --git a/.github/workflows/test-mutations.yml b/.github/workflows/test-mutations.yml new file mode 100644 index 00000000..1c916885 --- /dev/null +++ b/.github/workflows/test-mutations.yml @@ -0,0 +1,56 @@ +name: test-mutations + +on: + workflow_dispatch: + push: + branches: + - "master" # Run the workflow when pushing to the master branch + paths-ignore: + - "**.md" + - "**.png" + pull_request: + branches: + - "*" # Run the workflow for all pull requests + paths-ignore: + - "**.md" + - "**.png" + +env: + DOTNET_NOLOGO: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + Configuration: Release + Namespace: Objectivity.AutoFixture.XUnit2 + StrongNameKeyName: key.snk + +defaults: + run: + shell: pwsh + +jobs: + init: + runs-on: ubuntu-latest + steps: + - name: 📥 checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: 🖊️ materialize signing key + id: signing-key + run: | + $path = [IO.Path]::Combine("${{ github.workspace }}","src","${{ env.StrongNameKeyName }}") + [IO.File]::WriteAllBytes($path, [Convert]::FromBase64String("$env:StrongNameKey")) + "PATH=$path" >> $env:GITHUB_OUTPUT + env: + StrongNameKey: ${{ secrets.SIGNING_KEY }} + - name: 💾 install stryker.net + run: | + dotnet new tool-manifest + dotnet tool install --local dotnet-stryker + - name: 👾 test mutations + run: | + dotnet-stryker -s ./src/${{ env.Namespace }}.AutoMock.sln -r 'ClearText' + env: + CI: true + StrongNameKey: ${{ secrets.SIGNING_KEY }} + StrongNameKeyPath: ${{ steps.signing-key.outputs.PATH }} + From a284650d926f6bfe2667d2daf5babe6615a9a329 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Thu, 28 Sep 2023 10:47:33 +0200 Subject: [PATCH 05/10] Trigger dotnet-stryker properly --- .github/workflows/test-mutations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-mutations.yml b/.github/workflows/test-mutations.yml index 1c916885..c7c45605 100644 --- a/.github/workflows/test-mutations.yml +++ b/.github/workflows/test-mutations.yml @@ -48,7 +48,7 @@ jobs: dotnet tool install --local dotnet-stryker - name: 👾 test mutations run: | - dotnet-stryker -s ./src/${{ env.Namespace }}.AutoMock.sln -r 'ClearText' + dotnet tool run dotnet-stryker -s ./src/${{ env.Namespace }}.AutoMock.sln -r 'ClearText' env: CI: true StrongNameKey: ${{ secrets.SIGNING_KEY }} From 9f43b42fc64d8741bb8d67a5aa92073e9989c2fb Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Thu, 28 Sep 2023 10:50:05 +0200 Subject: [PATCH 06/10] Point to appropriate solution --- .github/workflows/test-mutations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-mutations.yml b/.github/workflows/test-mutations.yml index c7c45605..5346945e 100644 --- a/.github/workflows/test-mutations.yml +++ b/.github/workflows/test-mutations.yml @@ -48,7 +48,7 @@ jobs: dotnet tool install --local dotnet-stryker - name: 👾 test mutations run: | - dotnet tool run dotnet-stryker -s ./src/${{ env.Namespace }}.AutoMock.sln -r 'ClearText' + dotnet tool run dotnet-stryker -s ${{ github.workspace }}/src/${{ env.Namespace }}.AutoMock.sln -r 'ClearText' env: CI: true StrongNameKey: ${{ secrets.SIGNING_KEY }} From ae4e9b6e6bbef8816b912484e36dcd29806f7663 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Thu, 28 Sep 2023 10:56:36 +0200 Subject: [PATCH 07/10] Use working-directory --- .github/workflows/test-mutations.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-mutations.yml b/.github/workflows/test-mutations.yml index 5346945e..f888d0b4 100644 --- a/.github/workflows/test-mutations.yml +++ b/.github/workflows/test-mutations.yml @@ -47,8 +47,9 @@ jobs: dotnet new tool-manifest dotnet tool install --local dotnet-stryker - name: 👾 test mutations + working-directory: ./src run: | - dotnet tool run dotnet-stryker -s ${{ github.workspace }}/src/${{ env.Namespace }}.AutoMock.sln -r 'ClearText' + dotnet tool run dotnet-stryker -s ${{ env.Namespace }}.AutoMock.sln -r 'ClearText' env: CI: true StrongNameKey: ${{ secrets.SIGNING_KEY }} From 4e47602fbc446092be50bd6132fac62b2d5d397c Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Sat, 30 Sep 2023 08:38:54 +0200 Subject: [PATCH 08/10] Use dashboard reporter --- .github/workflows/test-mutations.yml | 3 +-- src/stryker-config.yaml | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 src/stryker-config.yaml diff --git a/.github/workflows/test-mutations.yml b/.github/workflows/test-mutations.yml index f888d0b4..c170d1bd 100644 --- a/.github/workflows/test-mutations.yml +++ b/.github/workflows/test-mutations.yml @@ -19,7 +19,6 @@ env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true Configuration: Release - Namespace: Objectivity.AutoFixture.XUnit2 StrongNameKeyName: key.snk defaults: @@ -49,7 +48,7 @@ jobs: - name: 👾 test mutations working-directory: ./src run: | - dotnet tool run dotnet-stryker -s ${{ env.Namespace }}.AutoMock.sln -r 'ClearText' + dotnet tool run dotnet-stryker -f stryker-config.yaml -r dashboard -v ${{ github.ref_name }} --dashboard-api-key ${{ secrets.STRYKER_API_KEY }} env: CI: true StrongNameKey: ${{ secrets.SIGNING_KEY }} diff --git a/src/stryker-config.yaml b/src/stryker-config.yaml new file mode 100644 index 00000000..584808f2 --- /dev/null +++ b/src/stryker-config.yaml @@ -0,0 +1,4 @@ +stryker-config: + solution: 'Objectivity.AutoFixture.XUnit2.AutoMock.sln' + project-info: + name: 'github.com/ObjectivityLtd/AutoFixture.XUnit2.AutoMock' \ No newline at end of file From 4d15042d25597be9b844a76f595ebd0d184fd6f3 Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Sat, 30 Sep 2023 08:54:44 +0200 Subject: [PATCH 09/10] Add mutation badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af309585..5d6ec93b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Objectivity.AutoFixture.XUnit2.AutoMock -[![CI/CD](https://github.com/ObjectivityLtd/AutoFixture.XUnit2.AutoMock/actions/workflows/cicd.yml/badge.svg?branch=master)](https://github.com/ObjectivityLtd/AutoFixture.XUnit2.AutoMock/actions/workflows/cicd.yml) [![codecov](https://codecov.io/gh/ObjectivityLtd/AutoFixture.XUnit2.AutoMock/branch/master/graph/badge.svg)](https://codecov.io/gh/ObjectivityLtd/AutoFixture.XUnit2.AutoMock) [![License: MIT](https://img.shields.io/github/license/ObjectivityLtd/AutoFixture.XUnit2.AutoMock?label=License&color=brightgreen)](https://opensource.org/licenses/MIT) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FObjectivityLtd%2FAutoFixture.XUnit2.AutoMock.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FObjectivityLtd%2FAutoFixture.XUnit2.AutoMock?ref=badge_shield) +[![CI/CD](https://github.com/ObjectivityLtd/AutoFixture.XUnit2.AutoMock/actions/workflows/cicd.yml/badge.svg?branch=master)](https://github.com/ObjectivityLtd/AutoFixture.XUnit2.AutoMock/actions/workflows/cicd.yml) [![codecov](https://codecov.io/gh/ObjectivityLtd/AutoFixture.XUnit2.AutoMock/branch/master/graph/badge.svg)](https://codecov.io/gh/ObjectivityLtd/AutoFixture.XUnit2.AutoMock) [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FObjectivityLtd%2FAutoFixture.XUnit2.AutoMock%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/ObjectivityLtd/AutoFixture.XUnit2.AutoMock/master) [![License: MIT](https://img.shields.io/github/license/ObjectivityLtd/AutoFixture.XUnit2.AutoMock?label=License&color=brightgreen)](https://opensource.org/licenses/MIT) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FObjectivityLtd%2FAutoFixture.XUnit2.AutoMock.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FObjectivityLtd%2FAutoFixture.XUnit2.AutoMock?ref=badge_shield) Accelerates preparation of mocked structures for unit tests under [XUnit2](http://xunit.github.io/) by configuring [AutoFixture](https://github.com/AutoFixture/AutoFixture) data generation to use a mocking library of your choice. Gracefully handles recursive structures by omitting recursions. From c823ca2d63f917ddbdb2efbf4155358f5dab80cd Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Sat, 30 Sep 2023 09:15:21 +0200 Subject: [PATCH 10/10] print debug info --- .github/workflows/test-mutations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-mutations.yml b/.github/workflows/test-mutations.yml index c170d1bd..1cd8b069 100644 --- a/.github/workflows/test-mutations.yml +++ b/.github/workflows/test-mutations.yml @@ -48,7 +48,7 @@ jobs: - name: 👾 test mutations working-directory: ./src run: | - dotnet tool run dotnet-stryker -f stryker-config.yaml -r dashboard -v ${{ github.ref_name }} --dashboard-api-key ${{ secrets.STRYKER_API_KEY }} + dotnet tool run dotnet-stryker -f stryker-config.yaml -r dashboard -v ${{ github.ref_name }} --dashboard-api-key ${{ secrets.STRYKER_API_KEY }} -V debug env: CI: true StrongNameKey: ${{ secrets.SIGNING_KEY }}