Метод 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
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
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

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