Skip to content

Conversation

@Keboo
Copy link
Collaborator

@Keboo Keboo commented Apr 7, 2022

This generator can generate argument null tests. These tests are often low value for developers to write, but not writing them can have a negative impact on projects where code coverage is measured. This is a compromise allowing AM to generate the tests out and let developers focus on writing higher value tests.

The trigger for this generator is a new ConstructorTestsAttribute. When this is placed on a class, the generator will generate out a partial class with tests for the constructors.

Sample showing the generated partial class that was generated for the xUnit project here:

namespace Moq.AutoMock.Generator.Example.xUnit
{
    partial class ControllerTests
    {
        partial void AutoMockerTestSetup(Moq.AutoMock.AutoMocker mocker, string testName);

        partial void ControllerConstructor_WithNullIService_ThrowsArgumentNullExceptionSetup(Moq.AutoMock.AutoMocker mocker);

        [global::Xunit.Fact]
        public void ControllerConstructor_WithNullIService_ThrowsArgumentNullException()
        {
            Moq.AutoMock.AutoMocker mocker = new Moq.AutoMock.AutoMocker();
            AutoMockerTestSetup(mocker, "ControllerConstructor_WithNullIService_ThrowsArgumentNullException");
            ControllerConstructor_WithNullIService_ThrowsArgumentNullExceptionSetup(mocker);
            System.ArgumentNullException ex = global::Xunit.Assert.Throws<System.ArgumentNullException>(() => _ = new global::Moq.AutoMock.Generator.Example.Controller(default(global::Moq.AutoMock.Generator.Example.IService)));
            global::Xunit.Assert.Equal("service", ex.ParamName);
        }

        partial void ControllerConstructor_WithNullIService1_ThrowsArgumentNullExceptionSetup(Moq.AutoMock.AutoMocker mocker);

        [global::Xunit.Fact]
        public void ControllerConstructor_WithNullIService1_ThrowsArgumentNullException()
        {
            Moq.AutoMock.AutoMocker mocker = new Moq.AutoMock.AutoMocker();
            AutoMockerTestSetup(mocker, "ControllerConstructor_WithNullIService1_ThrowsArgumentNullException");
            ControllerConstructor_WithNullIService1_ThrowsArgumentNullExceptionSetup(mocker);
            var service2 = mocker.Get<global::Moq.AutoMock.Generator.Example.IService>();
            System.ArgumentNullException ex = global::Xunit.Assert.Throws<System.ArgumentNullException>(() => _ = new global::Moq.AutoMock.Generator.Example.Controller(default(global::Moq.AutoMock.Generator.Example.IService),service2));
            global::Xunit.Assert.Equal("service1", ex.ParamName);
        }

        partial void ControllerConstructor_WithNullIService2_ThrowsArgumentNullExceptionSetup(Moq.AutoMock.AutoMocker mocker);

        [global::Xunit.Fact]
        public void ControllerConstructor_WithNullIService2_ThrowsArgumentNullException()
        {
            Moq.AutoMock.AutoMocker mocker = new Moq.AutoMock.AutoMocker();
            AutoMockerTestSetup(mocker, "ControllerConstructor_WithNullIService2_ThrowsArgumentNullException");
            ControllerConstructor_WithNullIService2_ThrowsArgumentNullExceptionSetup(mocker);
            var service1 = mocker.Get<global::Moq.AutoMock.Generator.Example.IService>();
            System.ArgumentNullException ex = global::Xunit.Assert.Throws<System.ArgumentNullException>(() => _ = new global::Moq.AutoMock.Generator.Example.Controller(service1,default(global::Moq.AutoMock.Generator.Example.IService)));
            global::Xunit.Assert.Equal("service2", ex.ParamName);
        }
        ...
    }
}

Outstanding work and questions:

  1. Needs to be appropriately packaged into the NuGet package
  2. Needs tests around failure cases
    • What happens if the class is not declared partial?
    • What happens if the TargetType is not specified on the attribute?
    • What happens if constructor arguments are simple value types (int, string, struct, etc)?
  3. Right now it supports generating tests for MSTest, xUnit, and NUnit. There is an Unknown case that could be more flushed out . This would simply run the same test, catching the ArgumentNullException and rethrowing if the parameter name was wrong. Because these methods would not be decorated with any attributes it would be up to the develop to appropriately invoke them (perhaps providing a helper method to invoke all of the generated methods?). Would this be a valuable addition?
  4. Should VB be supported? Right now it just bails if the language is not C#.
  5. Should this allow for some way to generate similar tests for method beyond just constructors?

Keboo and others added 5 commits March 10, 2022 15:17
Using lots of hard coded values for now
Still need to handle naming collisions
VB support?
TODO: Multiple constructors
Disable VB generation
@Keboo Keboo requested a review from adamhewitt627 April 7, 2022 16:37
@Keboo Keboo changed the title WIP creating source generator Creating source generator Apr 22, 2022
@Keboo
Copy link
Collaborator Author

Keboo commented Apr 22, 2022

@adamhewitt627 this is ready for review

@Keboo Keboo merged commit 85a400f into moq:master Apr 25, 2022
@Keboo Keboo deleted the testGenerator branch April 25, 2022 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants