Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutoMock.Mock<>() ignored in child lifetime scope #43

Closed
xzxzxc opened this issue Aug 24, 2021 · 4 comments · Fixed by #46
Closed

AutoMock.Mock<>() ignored in child lifetime scope #43

xzxzxc opened this issue Aug 24, 2021 · 4 comments · Fixed by #46

Comments

@xzxzxc
Copy link
Contributor

xzxzxc commented Aug 24, 2021

Describe the Bug

Setup of mock from AutoMock.Mock<>() is ignored in child lifetime scopes. But if you register mock using ContainerBuilder.RegisterMock() extension, it's not. This is because child scope resolves a new and so, an empty mock instance.

Steps to Reproduce

[Test]
public void ThisWorks()
{
    var registeredObjectMock = new Mock<object>();
    registeredObjectMock.Setup(o => o.GetHashCode()).Returns(1);
    var container = AutoMock.GetLoose(
        builder =>
        {
            builder.RegisterMock(registeredObjectMock);
        });
    var rootScope = container.Create<ILifetimeScope>();
    var childScope = rootScope.BeginLifetimeScope();

    var resolvedObject = childScope.Resolve<object>();

    // setup worked
    resolvedObject.GetHashCode().Should().Be(1);
}

[Test]
public void ThisDoesnt()
{
    var container = AutoMock.GetLoose();
    container.Mock<object>().Setup(o => o.GetHashCode()).Returns(1);
    var rootScope = container.Create<ILifetimeScope>();
    var childScope = rootScope.BeginLifetimeScope();

    var resolvedObject = childScope .Resolve<object>();

    // setup didn't work
    resolvedObject.GetHashCode().Should().Be(1);
}

Expected Behavior

Mock instance resolved from nested scope is the same as AutoMock.Mock<> returns.

Dependency Versions

Autofac.Extras.Moq package version 6.0.0
Autofac package version 6.2.0

@tillig
Copy link
Member

tillig commented Aug 24, 2021

Interesting. I can kinda see why this might be happening, but I'm not sure off the top of my head how to fix it. It doesn't appear any of the unit tests in here take into account child lifetime scopes. (I admit I also can't really quantify in cohesive language what I'm thinking about because some of it is gut feel and not researched, so I can't say "Oh, it's this line!" or something like that.)

I won't lie, while I see this is an issue, I'm not sure I have a bunch of time right now to dig into it. We'd welcome a pull request with a fix, or perhaps if one of the other maintainers here has time they can dive in. However, the PR is likely your quickest route to success.

@tougher
Copy link

tougher commented Apr 8, 2022

I just faced the same issue. It's pretty easy to work around it by doing:

AutoMock.Provide(new Mock<IService>().Object);

@jzabroski
Copy link

I might be interested in looking into this. To be honest, I've never used Autofac.Extras.Moq. I generally assume I am making a huge mistake in my code if I am passing around an ILifetimeScope. But... this problem has bit me a couple times in the last few years, and while I can work around it, I would rather my code just work the first time I write it.

But this is inherently an x-y problem for me. I came here because I am trying to avoid this problem:

System.NotSupportedException
Unsupported expression: x => x.Resolve<INameRepository>()
Extension methods (here: ResolutionExtensions.Resolve) may not be used in setup / verification expressions.

I run into this problem on code bases without a unit of work pattern, where the developers think its better for people to explicitly pass around ILifetimeScope than an IUnitOfWork instance. Without managing lifetime scopes, I can't manage connections. Without managing connections, I can't correctly thread parallel tasks and implement partial failure of large batches of data saves (because Entity Framework DbContext will become corrupt on the first exception).

@jzabroski
Copy link

I just faced the same issue. It's pretty easy to work around it by doing:

AutoMock.Provide(new Mock<IService>().Object);

-- @tougher

This workaround does not work as of Autofac v5.0.0 (release notes), because Provide was removed because it violates the decision to make the Container immutable.

My fix works - I also demonstrate using it with AutoMoq (AutoFixture + Moq)

// Fixture is an AutoFixture.IFixture instance, defined elsewhere
var savedNames = new List<Name>();
var nameRepository = Fixture.Freeze<Mock<INameRepository>>();
nameRepository.Setup(x => x.Save(It.IsAny<Name>()))
    .Callback((Name x) =>
    {
        savedNames.Add(x);
    });

var mock = AutoMock.GetLoose(cb => cb.RegisterInstance(nameRepository.Object));

var lifetimeScope = mock.Create<ILifetimeScope>();
Fixture.Inject(lifetimeScope);

var sut = Fixture.Create<RenameService>();
await sut.Rename();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants