Можно ли вызвать асинхронный обратный вызов с Moq, когда вызывается смоделированный метод? - PullRequest
0 голосов
/ 22 декабря 2018

Я высмеиваю некоторую реализацию с использованием Moq, и я хочу убедиться, что метод вызывается правильно на этом интерфейсе, проблема в том, что он выглядит примерно так:

public interface IToBeMocked {
    void DoThing(IParameter parameter);
}

public interface IParameter {
    Task<string> Content { get; }
}

Итак, я настроил свой макет:

var parameter = "ABC";
var mock = new Mock<IToBeMocked>();
mock
    .Setup(m => m.DoThing(It.IsAny<IParameter>()))
    .Callback<IParameter>(p async => (await p.Content).Should().Be(parameter));

new Processor(mock.Object).Process(parameter);

mock
    .Verify(m => m.DoThing(It.IsAny<IParameter>()), Times.Once);

К сожалению, этот тест уже проходит со следующей реализацией:

public class Processor {
    public Processor(IToBeMocked toBeMocked){
        _toBeMocked = toBeMocked;
    }

    public void Process(string parameter){
        _toBeMocked.DoThing(null);
    }
}

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

Есть ли в Moq какие-либо функции для ожидания асинхронного обратного вызова?

Редактировать

Кажется, есть некоторая путаница.Я надеюсь, что это проясняет проблему.

Я делаю TDD.Я реализовал простейшую оболочку кода для компиляции теста.Затем я написал тест, чтобы убедиться, что «ABC» является результатом Task, и настроил его запуск.Это проходит. Это проблема .Я хочу, чтобы тест не прошел, поэтому я могу реализовать «реальную» реализацию.

Редактировать 2

Чем больше я думаю об этом, тем больше я думаю, что это, вероятно, невозможно.Я открыл вопрос для репо: https://github.com/moq/moq4/issues/737, но я думал о реализации такой функции, как я писал запрос в ожидании подачи PR, и это кажется невозможным.Тем не менее, если у кого-то есть какие-либо идеи, я бы хотел услышать их здесь или в выпуске GitHub, и я буду держать оба места в курсе.На данный момент, я полагаю, мне придется использовать заглушку.

1 Ответ

0 голосов
/ 22 декабря 2018

Для обратного вызова требуется действие, вы пытаетесь выполнить асинхронную операцию в указанном обратном вызове, которая сводится к вызову async void.Исключения не могут быть перехвачены в таких ситуациях, когда они запускаются и забываются.

Ссылка Асинхронное / ожидание - Лучшие практики асинхронного программирования

Так что проблема не в Moqфреймворк, а не принятый подход.

Используйте обратный вызов, чтобы получить нужный параметр и поработать оттуда.

Просмотрите ход следующих тестов, чтобы увидеть, как подход TDD развивается с каждым тестом.

[TestClass]
public class MyTestClass {
    [Test]
    public void _DoThing_Should_Be_Invoked() {
        //Arrange            
        var parameter = "ABC";
        var mock = new Mock<IToBeMocked>();
        mock
            .Setup(m => m.DoThing(It.IsAny<IParameter>()));

        //Act
        new Processor(mock.Object).Process(parameter);

        //Assert            
        mock.Verify(m => m.DoThing(It.IsAny<IParameter>()), Times.Once);
    }

    [Test]
    public void _Parameter_Should_Not_Be_Null() {
        //Arrange
        IParameter actual = null;

        var parameter = "ABC";
        var mock = new Mock<IToBeMocked>();
        mock
            .Setup(m => m.DoThing(It.IsAny<IParameter>()))
            .Callback<IParameter>(p => actual = p);

        //Act
        new Processor(mock.Object).Process(parameter);

        //Assert
        actual.Should().NotBeNull();
        mock.Verify(m => m.DoThing(It.IsAny<IParameter>()), Times.Once);
    }

    [Test]
    public async Task _Parameter_Content_Should_Be_Expected() {
        //Arrange

        IParameter parameter = null;

        var expected = "ABC";
        var mock = new Mock<IToBeMocked>();
        mock
            .Setup(m => m.DoThing(It.IsAny<IParameter>()))
            .Callback<IParameter>(p => parameter = p);

        new Processor(mock.Object).Process(expected);

        parameter.Should().NotBeNull();

        //Act
        var actual = await parameter.Content;

        //Assert
        actual.Should().Be(expected);
        mock.Verify(m => m.DoThing(It.IsAny<IParameter>()), Times.Once);
    }
}
...