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

Moq setup mismatches generic methods with derived generic arguments #1157

Closed
svdsande opened this issue Apr 29, 2021 · 6 comments
Closed

Moq setup mismatches generic methods with derived generic arguments #1157

svdsande opened this issue Apr 29, 2021 · 6 comments

Comments

@svdsande
Copy link

Earlier this week I updated Moq and all of a sudden some of my unit tests failed. I have created a minimal reproducible scenario below.

The following works in Moq 4.13.1 and earlier, but is broken in Moq 4.16.1.

public class MoqTests
{
    private readonly Mock<IUnitOfWork> _uowMock;

    private readonly Mock<ISpecificRepository> _specificRepositoryMock;
    private readonly Mock<IRepository<Specific>> _genericRepositoryMock;

    public MoqTests()
    {
	_uowMock = new Mock<IUnitOfWork>();

	_specificRepositoryMock = new Mock<ISpecificRepository>(MockBehavior.Strict);
	_genericRepositoryMock = new Mock<IRepository<Specific>>(MockBehavior.Strict);
	_uowMock.Setup(t => t.Repository<ISpecificRepository>()).Returns(_specificRepositoryMock.Object);
	_uowMock.Setup(t => t.Repository<IRepository<Specific>>()).Returns(_genericRepositoryMock.Object);
    }

    [Fact]
    public void TestMockRepository()
    {
	_uowMock.Object.Repository<ISpecificRepository>();
    }
}

public interface IUnitOfWork
{
    T Repository<T>();
}

public interface IRepository<T>
{

}

public interface ISpecificRepository : IRepository<Specific>
{

}

public class Specific
{

}

Executing the TestMockRepository unit test results in the following error message:
System.InvalidCastException: 'Unable to cast object of type 'Castle.Proxies.IRepository`1Proxy' to type '.Foo.ISpecificRepository'.'

Any thoughts on why this has changed, or is there a possible work around?

@stakx
Copy link
Contributor

stakx commented Apr 29, 2021

Have you already tried swapping the order of your two setups? ISpecificRepository is a more specific type than IRepository<Specific>, and because you should always order from least specific to most specific (due to Moq prioritising newer setups over older ones), you should set it up last.

(In this case, by "more specific", I mean that a ISpecificRepository is a subtype of IRepository<Specific> but not vice versa.)

@svdsande
Copy link
Author

Thanks for the quick response 👍 The solution that you provided did work. I understand that it is important to structure the setups in a logical order, from least specific to most specific. Maybe there is also a possibility for a more robust solution? Since ordering the setups did work but this might be prone to errors

@stakx
Copy link
Contributor

stakx commented Apr 30, 2021

this might be prone to errors

Can you explain why you think so?

@svdsande
Copy link
Author

svdsande commented May 3, 2021

Right now I know that it is important to put the setups in the correct order. But from a developer standpoint, if for instance one of my colleagues doesn't know this he or she might be faced with an exception when creating new unit tests. I could imagine that the exception itself is not expected when looking at the current context. Therefore It would be convenient if Moq is able to return the desired object independent of the setup order. Hope this explains it a bit more.

@stakx
Copy link
Contributor

stakx commented May 3, 2021

if for instance one of my colleagues doesn't know this [...]

...then they should learn about it. If they get an exception that they didn't expect, or cannot make sense of, that is a good moment for them to start learning.

If you can steer them around difficult cases through conventions, that's of course great. But we developers need to know at least the basics of the tools that we rely on... there's no way around that.

Understand that the recent change that you've run into made Moq more consistent overall; the rule of thumb that more specific setups should follow general ones (because Moq prioritises newer setups) is now simply enforced more consistently. So there's actually less things to know about Moq than before!

@svdsande
Copy link
Author

Thanks for the clear explanation, it clarifies a lot for me! I agree that we as developers should understand the basics of the tools that we use. It is clear that this is not a bug but simply something that I misunderstood. Thanks for your help @stakx 👍

@stakx stakx closed this as completed May 13, 2021
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

No branches or pull requests

2 participants