From fb27821f37199b6e6388d687fe4eab23113da5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Zaj=C4=85c?= Date: Sat, 30 Sep 2023 09:16:15 +0200 Subject: [PATCH] Mutation testing (#64) * Improve tests by making them immune to mutations * Introduce mutations testing pipeline * Add mutation badge --- .github/workflows/test-mutations.yml | 56 ++++++++++++++++++ README.md | 2 +- .../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 +++ .../AutoDataAdapterAttributeTests.cs | 59 ++++++++++++++++++- .../Attributes/CustomizeWithAttributeTests.cs | 3 +- .../MemberAutoDataItemConverterTests.cs | 52 ++++++++++++---- src/stryker-config.yaml | 4 ++ 18 files changed, 267 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/test-mutations.yml 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 create mode 100644 src/stryker-config.yaml diff --git a/.github/workflows/test-mutations.yml b/.github/workflows/test-mutations.yml new file mode 100644 index 00000000..1cd8b069 --- /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 + 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 + 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 }} -V debug + env: + CI: true + StrongNameKey: ${{ secrets.SIGNING_KEY }} + StrongNameKeyPath: ${{ steps.signing-key.outputs.PATH }} + 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. 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; } + } +} 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()))); + } + } } } 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() 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