Метод Moq FindAsync возвращает значение null

Я пытался в течение нескольких дней, чтобы проверить метод удаления, но безуспешно.

После отладки кода я понял, что проблема в Метод FindAsync возвращает значение null и из-за этого тест попадает в состояние NotFound().

Поскольку я новичок в мире C#, .NET, EntityFramework и Moq, кто-нибудь может мне помочь?

  • Контроллер
        public async Task<IActionResult> DeleteTodoItem(long id)
        {
            var todoItem = await _context.TodoItems.FindAsync(id);
            if (todoItem == null)
            {
                return NotFound();
            }

            _context.TodoItems.Remove(todoItem);
            await _context.SaveChangesAsync();

            return NoContent();
        }
  • Контрольная работа
    [Fact]
    public async Task DeleteTodoItem_ShouldBeCallFindAsyncMethodOnce()
    {
        var todo = new TodoItem { Id = 1, Name = "test", IsComplete = true };
        
        var mockSet = new Mock<DbSet<TodoItem>>();

        var options = new DbContextOptionsBuilder<TodoContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .Options;

        var mockContext = new Mock<TodoContext>(options);
        mockContext.Setup(c => c.TodoItems).Returns(mockSet.Object);
        mockContext.Setup(c => c.TodoItems.FindAsync(1)).ReturnsAsync(todo);
        
        var service = new TodoItemsController(mockContext.Object);
        var deleteTodo = await service.DeleteTodoItem(1);
        
        mockSet.Verify(m => m.FindAsync(It.IsAny<TodoItem>()), Times.Once());
        
        
    }

Почему вы комбинируете Moq с базой данных в памяти? Вам не нужны оба. Только одно или другое.

mason 18.05.2022 00:08

На данный момент мое приложение использует память в качестве базы данных, и примеры, которые я видел, выглядели так:

Lucas Dittrich 18.05.2022 00:12

Я думаю, что ваш последний макет (на mockContext) должен быть на mockSet в методе FindAsync.

PeteGO 18.05.2022 00:12

Это все еще не меняет того факта, что вы должны использовать один или другой. Нет смысла использовать базу данных в памяти, если вы собираетесь выполнять вызовы Moq. И наоборот: нет смысла имитировать вызовы методов, если вы собираетесь использовать БД в памяти. Выберите один или другой.

mason 18.05.2022 00:15

Я попытался сделать это, как показано ниже, но все равно возвращает ноль: ` var mockSet = new Mock<DbSet<TodoItem>>(); mockSet.Setup(c => c.FindAsync(1)).ReturnsAsync(todo); `

Lucas Dittrich 18.05.2022 00:16

Я понимаю вашу точку зрения @mason, и локально я уже удалил часть базы данных (... UseInMemory (...)), но в любом случае Финдасинк по-прежнему возвращает значение null

Lucas Dittrich 18.05.2022 00:21

Я превратил превратил ваш код в правильный минимальный воспроизводимый пример. Это гораздо полезнее, потому что теперь любой может добавить его в новое консольное приложение или dotnetfiddle и получить точно такую ​​же ошибку, как и вы.

mason 18.05.2022 00:43

Спасибо за попытку помочь мне, @mason. Оценив ваш код и появившуюся ошибку, я заметил, что ваш DbSet не соответствует virtual, что является директивой Moq. И еще раз спасибо, что нашли время для разработки примера, и я извиняюсь за то, что не сделал этого, как вы могли заметить, я здесь новичок, и я ценю ваш совет: D

Lucas Dittrich 18.05.2022 00:47
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
1
8
20
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы настраиваете макет и проверяете макет для FindAsync на int, когда ваш контроллер передает его long. Поэтому вам нужно настроить макет и проверку в формате long, а не int. А также настройте макет FindAsync для DbSet, а не для DbContext.

Например:

var todo = new TodoItem { Id = 1, Name = "test", IsComplete = true };
        
var mockSet = new Mock<DbSet<TodoItem>>();
mockSet.Setup(s => s.FindAsync(1L)).ReturnsAsync(todo);

var mockContext = new Mock<TodoContext>();
mockContext.Setup(c => c.TodoItems).Returns(mockSet.Object);
        
var service = new TodoItemsController(mockContext.Object);
var deleteTodo = await service.DeleteTodoItem(1);
        
mockSet.Verify(m => m.FindAsync(1L), Times.Once());

Полный рабочий образец здесь. Полный код, используемый для проверки ниже.

// Need package reference to Microsoft.EntityFrameworkCore v6.0.5
// Need package reference to Moq v4.18.1
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Moq;
                    
public class Program
{
    public static async Task Main()
    {
        var todo = new TodoItem { Id = 1, Name = "test", IsComplete = true };
        
        var mockSet = new Mock<DbSet<TodoItem>>();
        mockSet.Setup(s => s.FindAsync(1L)).ReturnsAsync(todo);

        var mockContext = new Mock<TodoContext>();
        mockContext.Setup(c => c.TodoItems).Returns(mockSet.Object);
        
        var service = new TodoItemsController(mockContext.Object);
        var deleteTodo = await service.DeleteTodoItem(1);
        
        mockSet.Verify(m => m.FindAsync(1L), Times.Once());
        Console.WriteLine("Test complete without error");
    }
}

public class TodoContext : DbContext
{
    public virtual DbSet<TodoItem> TodoItems { get; set; }
}

public class TodoItem
{
    public int Id { get; set; }
    
    public string Name { get; set; }
    
    public bool IsComplete { get; set; }
}

public class TodoItemsController
{
    readonly TodoContext _context;
    
    public TodoItemsController(TodoContext context)
    {
        _context = context;
    }
    
    public async Task<IActionResult> DeleteTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        _context.TodoItems.Remove(todoItem);
        await _context.SaveChangesAsync();

        return NoContent();
    }
    
    public NotFoundResult NotFound() { return new NotFoundResult(); }
    
    public NoContentResult NoContent() { return new NoContentResult(); }
}

public interface IActionResult{}

public class NotFoundResult : IActionResult {}

public class NoContentResult : IActionResult {}

Обратите внимание, что этот тест не кажется особенно полезным. Обычно при модульном тестировании мы делаем утверждения о результате, а не о деталях реализации. Нет необходимости утверждать, что FindAsync был вызван один раз. Это только делает тест более хрупким. Если вы выполняете модульное тестирование метода действия, вам нужно убедиться, что вы получаете NotFoundResult при передаче несуществующего элемента и NoContentResult при передаче существующего элемента, а соответствующий элемент удаляется. из DbSet. Использование DbContext в памяти, а не насмешка над ним, вероятно, сделает это проще.

большое спасибо за помощь @mason, спас мою учебу здесь

Lucas Dittrich 18.05.2022 00:58

Другие вопросы по теме