Настройка Moq для интерфейса с аргументом функции

Я хочу написать UnitTest с Moq 4.14.7. Тестируемый метод заключается в следующем:

public IFoo Get(Guid id)
{
    Func<IFoo> getData = () => _repository.Get(id);
    if (_cache.TryGetAndSet(vaultId, getData, out var result))
    {
        return result;
    }
}

Чтобы кратко объяснить. У меня есть общий класс кеша. Он проверяет в кеше, существует ли сущность с переданным guid. Если это не так, вызывается репозиторий.

Теперь у меня есть следующие настройки для кеша:

_cache.Setup
(
    x => x.TryGetAndSet
    (
        _vaultId,
        It.IsAny<Func<IFoo>>(),
        out foo
    )
)
.Returns(true);

Тест пока работает, но 100% покрытия я им не добиваюсь, потому что не покрыта функция.

Есть ли лучший способ, чем работать с It.IsAny и получить 100% покрытие?


person BenGeba    schedule 18.01.2021    source источник
comment
Этот тест покрывает 100% кода, который вы показали. Что означает, потому что функция не покрыта? Я предполагаю, что вам нужен еще один тест, который возвращает false?   -  person Liam    schedule 18.01.2021
comment
Нет, с помощью инструмента ReSharper UnitTestCoverage достигается покрытие только 88 %, а функция отображается как не покрытая.   -  person BenGeba    schedule 18.01.2021
comment
но разве это не то, что вы хотите? Так почему это вообще должно быть покрыто?   -  person HimBromBeere    schedule 18.01.2021
comment
Ну, без копии этого инструмента я не знаю, на что он жалуется. Если вы добавите еще один тест, который возвращает false, перестанет ли он жаловаться? Кстати, 100% покрытие кода — это нонсенс   -  person Liam    schedule 18.01.2021
comment
ваша исходная GetData-func наверняка никогда не выполняется - и, следовательно, не покрывается - потому что вы издеваетесь над единственной вызывающей функцией - TryGetAndSet.   -  person HimBromBeere    schedule 18.01.2021
comment
@HimBromBeere, если я сделаю _repo.Setup(x => x.Get(id)).Returns(It.IsAny<IFoo>); раньше, он все равно не будет вызван, потому что у меня есть It.IsAny<Func<IFoo>>() в макете кеша.   -  person BenGeba    schedule 18.01.2021
comment
@Liam Нет, к сожалению, он все еще жалуется.   -  person BenGeba    schedule 18.01.2021
comment
делегат никогда не будет выполнен, потому что единственная функция, которая когда-либо вызывала его, имитируется. По сути, вы говорите: замените каждый вызов TryGetAndSet чем-то совершенно другим, который вообще не будет касаться делегата.   -  person HimBromBeere    schedule 18.01.2021
comment
Я также пробовал использовать макет кеша вместо It.IsAny<> a () => _repository.Setup(m => m.Get(id)).Returns(foo), но он ожидает IFoo в качестве возвращаемого значения, но получает репо.   -  person BenGeba    schedule 18.01.2021


Ответы (1)


Захватите аргументы, переданные члену, и вызовите функцию по желанию.

//...

IFoo mockedFoo = Mock.Of<IFoo>();

IFoo foo = null;

_repo.Setup(_ => _.Get(It.IsAny<Guid>()).Returns(mockedFoo);

_cache
    .Setup(_ => _.TryGetAndSet(It.IsAny<Guid>(), It.IsAny<Func<IFoo>>(), out foo))
    .Returns((Guid vid, Func<IFoo> func, IFoo f) => {
        foo = func(); //<-- invoke the function and assign to out parameter.
        return true;
    });

//...

//Assertion can later verify that the repository.Get was invoked
//Assertion can later assert that foo equals mockedFoo

TryGetAndSet вернет true, а аргумент out вернет mockedFoo, ленивую оценку

Ссылка на Быстрый старт Moq

person Nkosi    schedule 18.01.2021
comment
Спасибо, отлично сработало :) - person BenGeba; 18.01.2021