I have the following interface with a generic method.
public interface IRepository {
Task<TResult> GetByIdAsync<TResult>(
int id,
Expression<Func<Entity, TResult>> expression,
TResult defaultValue = default);
}
In some parts of the code expression returns Tuple and anonymous type.
Example
var _repository = ServiceProvider.GetRequiredService<IRepository>();
var data = await _repository.GetByIdAsync(id, pr => Tuple.Create(pr, pr.SelfRefObject));
var data = await _repository.GetByIdAsync(id, pr => new { pr.SelfRefObjectId });
Is it possible to mock the method so it returns specified value with mentioned types?
Example
var _repositoryMock = new Mock<IRepository>();
var anonymousReturnValue = new { SelfRefObjectId = 1 };
_repositoryMock
.Setup(pr => pr.GetByIdAsync(
entityId,
pr => new { pr.SelfRefObjectId },
default))
.ReturnsAsync(anonymousReturnValue)
.Verifiable();
var tupleReturnValue = Tuple.Create(new Entity(), new Entity());
_repositoryMock
.Setup(pr => pr.GetByIdAsync(
entityId,
pr => Tuple.Create(pr, pr.SelfRefObject),
default))
.ReturnsAsync(tupleReturnValue)
.Verifiable();
Thanks in advance.
In short: according to my knowledge you can't do this.
There are two really useful helper types: It.IsAnyType
and It.IsSubType<T>
.
During the setup phase you could use them to tell that your function accepts anything:
_repositoryMock
.Setup(repo => repo.GetByIdAsync(
It.IsAny<int>(),
It.IsAny<Expression<Func<Entity, It.IsAnyType>>>(),
It.IsAny<It.IsAnyType>()))
.Verifiable();
Or it accepts only anonymous type or tuple:
_repositoryMock
.Setup(repo => repo.GetByIdAsync(
It.IsAny<int>(),
It.Is<Expression<Func<Entity, It.IsAnyType>>>((o, type) => type.IsAnonymousType()),
It.Is<It.IsAnyType>((o, type) => type.IsAnonymousType())))
.Verifiable();
_repositoryMock
.Setup(repo => repo.GetByIdAsync(
It.IsAny<int>(),
It.Is<Expression<Func<Entity, It.IsAnyType>>>((o, type) => type.IsTuple()),
It.Is<It.IsAnyType>((o, type) => type.IsTuple())))
.Verifiable();
Helper methods:
public static class TypeExtension
{
public static bool IsAnonymousType(this Type type)
{
var hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any();
var nameContainsAnonymousType = type.FullName != null && type.FullName.Contains("AnonymousType");
return hasCompilerGeneratedAttribute && nameContainsAnonymousType;
}
public static bool IsTuple(this Type type)
{
if (!type.IsGenericType) return false;
var openType = type.GetGenericTypeDefinition();
return openType == typeof(Tuple<>) || openType == typeof(Tuple<,>)
|| openType == typeof(ValueTuple<>) || openType == typeof(ValueTuple<,>);
}
}
The problem comes when you need to specify the Return/ReturnAsync
part.
_repositoryMock
.Setup(repo => repo.GetByIdAsync(
It.IsAny<int>(),
It.Is<Expression<Func<Entity, It.IsAnyType>>>((o, type) => type.IsTuple()),
It.Is<It.IsAnyType>((o, type) => type.IsTuple())))
.Returns(valueFunction: () => Task.FromResult(tupleReturnValue));
It won't compile because it can't convert Tuple<Entity, Entity>
to Mock.It.IsAnyType
Severity Code Description Project File Line Suppression State Error CS0029 Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Tuple<anonym.Entity, anonym.Entity>>' to 'System.Threading.Tasks.Task<Moq.It.IsAnyType>'
Even the related github issue is closed the Type resolver does not seems to work.
Alternative approach could be making use of DefaultValueProvider
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.