0

In our class constructor, we had multiple classes dependencies. as per Automoq documentation, we should have dependency of interface or abstraction.

Code Setup

  • System Under Test Class have dependency of ManageLocationRepository as class dependency, demonstrated in below code snippet.
public class CityEventListener : IEvent<LocationChangeEventData>
    {
        private readonly ILocationAdapterCaller _locationAdapterCaller;
        private readonly ManageLocationRepository _managerLocationRepository;

        public CityEventListener(ILocationAdapterCaller locationAdapterCaller, ManageLocationRepository managerLocationRepository)
        {
            _locationAdapterCaller = locationAdapterCaller;
            _managerLocationRepository = managerLocationRepository;
        }

        public async Task<bool> ProcessEvent(LocationChangeEventData eventData)
        {
        }
    }

Test Case for SUT class -

[Theory(DisplayName = "Valid value test")]
        [ClassAutoMoqData(typeof(ValidValueTests))]
        public async Task ProcessEvent_WithCreateOrUpdateOperation_CallsUpsertCityAndReturnsResult(ExpectedValueTestData<Parameters, bool> data,
        [Frozen] Mock<ILocationAdapterCaller> locationAdapterCallerMock, [Frozen] Mock<ManageLocationRepository> managerLocationRepositoryMock,
        CityEventListener sut)
        {
            // fakes
            var cityDetail = _fixture.Build<CityDetail>()
                                    .With(x => x.Id, data.Params.locationChangeEventData.Id).Create();

            // Arrange
            locationAdapterCallerMock.Setup(mock => mock.GetCityDetail(data.Params.locationChangeEventData.Id))
                .ReturnsAsync(cityDetail).Verifiable();

            managerLocationRepositoryMock
                .Setup(mock => mock.UpsertCity(cityDetail))
                .ReturnsAsync(data.ExpectedValue).Verifiable();

            var result = await sut.ProcessEvent(data.Params.locationChangeEventData);

            // Assert
            using (new AssertionScope())
            {
                Assert.IsType<bool>(result);
                Assert.Equal(data.ExpectedValue, result);
                locationAdapterCallerMock.Verify();
                managerLocationRepositoryMock.Verify();
            }
        }

Support for Class AutoMoq dependencies

public class ClassAutoMoqDataAttribute : CompositeDataAttribute
    {
        public ClassAutoMoqDataAttribute(Type values)
            : base(new ClassDataAttribute(values), new AutoMoqDataAttribute())
        {
        }
    }

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute() : base(() =>
    {
        var fixture = new Fixture().Customize(new CompositeCustomization(
            new AutoMoqCustomization() { ConfigureMembers = true, GenerateDelegates = true },
            new SupportMutableValueTypesCustomization()));

        fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList().ForEach(b => fixture.Behaviors.Remove(b));
        fixture.Behaviors.Add(new OmitOnRecursionBehavior());

        return fixture;
    })
    {
    }
}

Problem: Although the configuration in the above illustration may automoq class dependencies, but I am unable to obtain a frozen instance of the same class.

How can I use the class automoq attribute to better achieve automoq frozen dependencies?

Sanjay Soni
  • 201
  • 1
  • 13

1 Answers1

1

I solved my problem by taking two actions -

  1. mark concrete class method virtual
  2. create custom class automoq attribute to freeze dependencies
public class CityEventListenerClassAutoMoqAttribute : CompositeDataAttribute
   {
       public CityEventListenerClassAutoMoqAttribute(Type values)
           : base(new ClassDataAttribute(values), new CityEventListenerAutoMoqAttribute())
       {
       }
   }

   public class CityEventListenerAutoMoqAttribute : AutoDataAttribute
   {
       public CityEventListenerAutoMoqAttribute()
           : base(() =>
   {
       var fixture = new Fixture().Customize(new CompositeCustomization(
           new AutoMoqCustomization() { ConfigureMembers = true, GenerateDelegates = true },
           new SupportMutableValueTypesCustomization()));
       var managerLocationRepositoryMock =
               fixture.Freeze<Mock<ManageLocationRepository>>();
       fixture.Inject(managerLocationRepositoryMock.Object);
       return fixture;
   })
       {
       }
   }

Now my test case looks like this -

[Theory(DisplayName = "Valid value test")]
        [CityEventListenerClassAutoMoq(typeof(ValidValueTests))]
        public async Task ProcessEvent_WithCreateOrUpdateOperation_CallsUpsertCityAndReturnsResult(ExpectedValueTestData<Parameters, bool> data,
        [Frozen] Mock<ILocationAdapterCaller> locationAdapterCallerMock, [Frozen] Mock<ManageLocationRepository> managerLocationRepositoryMock,
        CityEventListener sut)
        {
            // fakes
            var cityDetail = _fixture.Build<CityDetail>()
                                    .With(x => x.Id, data.Params.locationChangeEventData.Id).Create();

            // Arrange
            locationAdapterCallerMock.Setup(mock => mock.GetCityDetail(data.Params.locationChangeEventData.Id))
                .ReturnsAsync(cityDetail).Verifiable();

            managerLocationRepositoryMock
                .Setup(mock => mock.UpsertCity(cityDetail))
                .ReturnsAsync(data.ExpectedValue).Verifiable();

            var result = await sut.ProcessEvent(data.Params.locationChangeEventData);

            // Assert
            using (new AssertionScope())
            {
                Assert.IsType<bool>(result);
                Assert.Equal(data.ExpectedValue, result);
                locationAdapterCallerMock.Verify();
                managerLocationRepositoryMock.Verify();
            }
        }

Do let me know if I can improve anything in my approach.

Sanjay Soni
  • 201
  • 1
  • 13