У меня есть приложение С#, в котором я создаю некоторые тесты, используя Moq. Для одной из моих конечных точек пользователь может передать один из нескольких параметров, это используется для построения лямбды, которая запрашивает базу данных:

var existingCriteria = await _criteriaService.GetCriteriaBy(e =>
    e.IsActive &&
    e.Crop == request.Crop &&
    e.Region == request.Region &&
    e.Market == request.Market &&
    e.ProductStage == request.ProductStage &&
    e.Trait == request.Trait &&
    e.HarvestType == request.HarvestType
);

Для моего конкретного теста мне важны только 2 параметра, и я использую этот ввод для GetCriteriaBy.

var request = new CriteriaRequest
{
    Crop = "Corn",
    Region = "NA"
};

var response = await new CriteriaController(_criteriaService.Object).AddCriteria(request);

Затем моя фиктивная служба настроена следующим образом:

Expression<Func<Criteria, bool>> matchExpression = x =>
    x.IsActive == true &&
    x.Crop == "Corn" &&
    x.Region == "NA" &&
    x.Market == null && 
    x.ProductStage == null &&
    x.Trait == null && 
    x.HarvestType == null; 

mock.Setup(cr => cr.GetCriteriaBy(It.Is<Expression<Func<Criteria, bool>>>(x => Lambda.Equals(x, matchExpression))))
    .ReturnsAsync((Expression<Func<Criteria, bool>> where) =>
    {
        return new List<CriteriaResponse>() {
            new CriteriaResponse {
                Id = 1,
                Crop = "Corn",
                Region = "NA"
            }
        };
    });

Но он никогда не ловит мой тестовый пример в Setup. Если я переключу It.Is на It.IsAny, это сработает, но тогда все мои тесты перехватываются этой установкой, и я хочу, чтобы здесь давали ответ только некоторые тесты.

Что я могу сделать, чтобы моя функция настройки работала должным образом?

0
A. Hasemeyer 30 Май 2023 в 17:59
Вместо того, чтобы пытаться сопоставить лямбду, вы можете использовать ее в своем ReturnsAsync, чтобы определить, что возвращать. Создайте List<CriteriaReponse> всех тестовых ответов и просто выполните Where(where), чтобы определить, что нужно вернуть.
 – 
juharr
30 Май 2023 в 18:12
Проблема в том, что возвращаемый тип отличается от типа where. Например, он использует Criteria для запроса, но возвращает CriteriaResponse. Если есть способ разобрать запрос where, который мог бы работать?
 – 
A. Hasemeyer
30 Май 2023 в 18:18
Вы изучали, как на самом деле работает Lambda.Equals()? (я не знаю класс Lambda и не могу найти для него никакой документации)
 – 
Astrid E.
30 Май 2023 в 20:58

3 ответа

Все еще ищу лучшее решение, но вот что я использовал, чтобы заставить его работать:

mock.Setup(cr => cr.GetCriteriaBy(It.IsAny<Expression<Func<Criteria, bool>>>()))
    .ReturnsAsync((Expression<Func<Criteria, bool>> where) =>
    {
        var criteria = new List<Criteria>()
        {
            new Criteria(){
                Id = 1,
                Crop = "Corn",
                Region = "NA"
            }
        };

        IQueryable<Criteria> query = criteria.AsQueryable();
        var filtered = query.Where(where);

        var response = new List<CriteriaResponse>();
        foreach (var item in filtered)
        {
            response.Add(new CriteriaResponse()
            {
                Id = item.Id,
                Crop = item.Crop,
                Region = item.Region
            });
        }

        return response;
    });

Кажется, что это слишком много кода для репликации чего-то, что должно работать в Moq.

0
A. Hasemeyer 30 Май 2023 в 18:31

Я подозреваю, что это происходит из-за несоответствия в лямбда-выражении. Например, IsActive действительно должно быть true на основе критериев поиска?

Попробуйте просто:

Expression<Func<Criteria, bool>> matchExpression = x =>
    x.Crop == "Corn" &&
    x.Region == "NA"; 

Если вы сузите область действия выражения соответствия, ваши шансы на попадание возрастут.

0
beautifulcoder 30 Май 2023 в 18:55
Я попытался сузить поля, и IsActive == true был одной из моих попыток настроить ввод, чтобы он соответствовал, но безуспешно. В моем контроллере он сопоставляет request со всеми полями в лямбде, даже если они пустые. Я предполагаю, что между лямбдами есть некоторая мета-разница, но я не могу ее найти.
 – 
A. Hasemeyer
30 Май 2023 в 19:13

Я сомневаюсь, что это то, что вы изначально хотели, но я думаю, что вы можете этого добиться (с немного меньшим количеством кода, чем в ваш собственный ответ), скомпилировав выражение и затем вызвав его с объектом, который подходит для фильтрации.

Что-то вроде:

mock.Setup(cr => cr.GetCriteriaBy(It.IsAny<Expression<Func<Criteria, bool>>>()))
    .ReturnsAsync((Expression<Func<Criteria, bool>> expr) =>
    {
        var isCornMatch = expr.Compile().Invoke(new Criteria { IsActive = true, Crop = "Corn" });

        if (isCornMatch)
        {
            return new List<CriteriaResponse>
            {
                new CriteriaResponse {
                    Id = 1,
                    Crop = "Corn",
                    Region = "NA"
                }
            };
        }
        
        return new List<CriteriaResponse>();
    });
0
Astrid E. 30 Май 2023 в 22:04