簡體   English   中英

使用表達式模擬泛型方法中的泛型返回類型

[英]Mocking generic return type in generic method with a expression

我有以下帶有通用方法的接口。

public interface IRepository {
  Task<TResult> GetByIdAsync<TResult>(
    int id,
    Expression<Func<Entity, TResult>> expression,
    TResult defaultValue = default);
}

在代碼表達式的某些部分返回元組和匿名類型。

例子

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 });

是否可以模擬該方法,以便它返回指定類型的指定值?

例子

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();

提前致謝。

簡而言之:據我所知,你不能這樣做。

有兩種非常有用的輔助類型: It.IsAnyTypeIt.IsSubType<T>

在設置階段,您可以使用它們來告訴您的函數接受任何內容:

_repositoryMock
    .Setup(repo => repo.GetByIdAsync(
        It.IsAny<int>(), 
        It.IsAny<Expression<Func<Entity, It.IsAnyType>>>(), 
        It.IsAny<It.IsAnyType>()))
    .Verifiable();

或者它只接受匿名類型或元組:

_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();

輔助方法:

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<,>);
    }
}

當您需要指定Return/ReturnAsync部分時,問題就出現了。

_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));

它不會編譯,因為它不能將Tuple<Entity, Entity>轉換為Mock.It.IsAnyType

嚴重性代碼描述項目文件行抑制狀態錯誤 CS0029 無法將類型“System.Threading.Tasks.Task<System.Tuple<anonym.Entity, anonym.Entity>>”隱式轉換為“System.Threading.Tasks.Task<Moq.It .IsAnyType>'

即使相關的github 問題已關閉,類型解析器似乎也不起作用。


替代方法可以使用DefaultValueProvider

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM