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

"Did not find a best constructor concrete type" error after updating to 2.1 #88

Closed
Piedone opened this issue Sep 15, 2020 · 8 comments
Closed
Labels

Comments

@Piedone
Copy link

Piedone commented Sep 15, 2020

This started happening with 2.1, the same code is fine with 2.0.1.

If a class has a constructor parameter of type Microsoft.AspNetCore.Http.HttpRequest or Microsoft.AspNetCore.Routing.LinkGenerator (note that both are concrete types) then exceptions of the following kind are thrown:

System.ArgumentException : Did not find a best constructor for `Microsoft.AspNetCore.Http.HttpRequest` (Parameter 'type')
at Moq.AutoMock.ConstructorSelector.SelectCtor(Type type, Type[] existingTypes, BindingFlags bindingFlags)
at Moq.AutoMock.AutoMocker.CreateArguments(Type type, ObjectGraphContext context)
at Moq.AutoMock.Resolvers.MockResolver.Resolve(MockResolutionContext context)
at Moq.AutoMock.AutoMocker.Resolve(Type serviceType, Object initialValue, ObjectGraphContext resolutionContext)
at Moq.AutoMock.AutoMocker.Resolve(Type serviceType, ObjectGraphContext resolutionContext)
at Moq.AutoMock.AutoMocker.GetMockImplementation(Type serviceType)
at Moq.AutoMock.AutoMocker.GetMock[TService]()
at MyProject.Tests.UnitTests.Services.MyClassTests.<>c__DisplayClass10_0.<CreateClaimsPrincipal>b__0(AutoMocker mocker) in C:\Agent3\work\FINI\src\Modules\MyProject\Tests\MyProject.Tests\UnitTests\Services\MyClassTests.cs:line 170
at Lombiq.Tests.Helpers.MockHelper.CreateAutoMockerInstance[T](Action`1 configurator, AutoMocker& mocker) in C:\Agent3\work\FINI\test\Lombiq.Tests\Helpers\MockHelper.cs:line 42
at MyProject.Tests.UnitTests.Services.MyClassTests.CreateClaimsPrincipal(User user, UserVerificationSettings settings, IDictionary`2 cookies, UserProfilePart userProfilePart) in C:\Agent3\work\FINI\src\Modules\MyProject\Tests\MyProject.Tests\UnitTests\Services\MyClassTests.cs:line 137
at MyProject.Tests.UnitTests.Services.MyClassTests.DaysSinceLastVerificationClaimShouldBeAccurate() in C:\Agent3\work\FINI\src\Modules\MyProject\Tests\MyProject.Tests\UnitTests\Services\MyClassTests.cs:line 102

I'd guess this has to do something with #86.

Piedone added a commit to Lombiq/Testing-Toolbox that referenced this issue Sep 15, 2020
@adamhewitt627
Copy link
Collaborator

This ends up being because we don't include the protected constructor of HttpRequest in the default resolution here:

ConstructorInfo? best = null;
foreach (var constructor in type.GetConstructors(bindingFlags))
{
if (IsBetterChoice(constructor))
best = constructor;
}
return best ?? throw new ArgumentException($"Did not find a best constructor for `{type}`", nameof(type));
. Specifically, bindingFlags == Instance | Public.

I also found that there is still a failure to resolve in 2.0, it is just delayed. For example,

public class SomeController
{
    public SomeController(HttpRequest request) { }
}


// As noted, this works in 2.0, but throws a stack trace like the above in 2.1
var controllerMock = mocker.GetMock<SomeController>();

// However, this throws in 2.0 resolving Object because it can't find a valid constructor on HttpRequest
Sut(controllerMock.Object);

@Piedone
Copy link
Author

Piedone commented Sep 16, 2020

Thank you for your quick reply, Adam!

@lkurzyniec
Copy link

When we can expect stable version with that fix?

@adamhewitt627
Copy link
Collaborator

Sorry, I've been in the midst of a job change and didn't mean to drop this. Testing aside, it could be as simple as changing the false to true here. Maybe that's the better default, though in other methods we've exposed that bool up the call stack, such as CreateInstance(bool enablePrivate).

private Mock GetMockImplementation(Type serviceType)
{
if (!_typeMap.TryGetValue(serviceType, out var instance) || instance is null)
instance = _typeMap[serviceType] = Resolve(serviceType, new ObjectGraphContext(false));

@Piedone
Copy link
Author

Piedone commented Dec 10, 2020

Thank you @adamhewitt627! Does this mean after updating AutoMocker is supposed to work as with 2.0, or is some configuration needed?

@adamhewitt627
Copy link
Collaborator

The issue is the exclusion of non-public constructors when it attempts to create the dependency. To align with the CreateInstance call, you do need to pass a flag:

IService service = mocker.GetMock<IService>(enablePrivate: true);

@Piedone
Copy link
Author

Piedone commented Dec 10, 2020

I see, thank you! Updated and works flawlessly.

@eddex
Copy link
Contributor

eddex commented Apr 13, 2021

Just stumbled upon this. I was refactoring some tests to use AutoMocker.GetMock() instead of new Mock() and my tests started failing with the exception: "System.ArgumentException : Did not find a best constructor for Microsoft.AspNetCore.Http.HttpRequest (Parameter 'type')".

[Fact] // fails
public void UseAutoMocker()
{
    this.AutoMocker.GetMock<HttpRequest>();
}

[Fact] // works
public void UseMock()
{
    new Mock<HttpRequest>();
}

Simply adding the enhablePrivate: true parameter in the constructor fixed the problem.

[Fact] // works
public void UseAutoMocker()
{
    this.AutoMocker.GetMock<HttpRequest>(true);
}

Thanks @adamhewitt627! 🎉

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

No branches or pull requests

4 participants