Вызов функции Moq всегда возвращает значение, даже если ввод не соответствует фиктивным входным значениям

Я издеваюсь над вызовом функции с двумя аргументами. 1. входной неизменяемый объект класса 2. выходной параметр.

образец кода:

Mock<ISample> mockSample = new Mock<ISample>();

SampleClass MyKey = new SampleClass() { prop1 = 1 };
SampleOutput output = new SampleOutput() { prop2 = 2 };
mockSample.setup(s => s.SampleMethod(It.is<SampleKey>(t => t.Equals(MyKey)), 
out sampleOut))).Returns(true);

в фактическом выполнении кода эта фиктивная функция возвращает правильное значение, если ключ совпадает с фиктивным ключом. Однако я вижу проблему, заключающуюся в том, что эта фиктивная функция возвращает то же значение, даже если ключ не совпадает.

Любые входы?

Добавляем код ключа:

public class Key
{
    public readonly DateTime prop1;
    public readonly string prop2;
    public Key(DateTime prop1, string prop2)
    {
        this.prop1 = prop1;
        this.prop2 = prop2;
    }
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        if (ReferenceEquals(this, obj))
            return true;

        Key other = obj as Key;

        return this.prop1 == other.prop1 && string.Compare(this.prop2, other.prop2);
    }

    public override int GetHashCode()
    {
        return prop1.GetHashCode() ^ prop2.GetHashCode();
    }
}

person Jack Sparrow    schedule 07.09.2018    source источник
comment
Возможный дубликат Назначение параметров out/ref в Moq   -  person Richardissimo    schedule 08.09.2018
comment
Строка return this.prop1 == other.prop1 && string.Compare(this.prop2, other.prop2); вызывает ошибку Error CS0019 Operator '&&' cannot be applied to operands of type 'bool' and 'int'   -  person KozhevnikovDmitry    schedule 10.09.2018
comment
Использование .Compare для проверки на равенство (вместо сортировки/упорядочения) — это плохо. Если причина в том, что вам нужно сравнение по культуре (а не по порядковому номеру), вы можете использовать string.Equals(this.prop2, other.prop2, StringComparison.CurrentCulture). Конечно, если вам нужно порядковое сравнение, вы просто скажете this.prop2 == other.prop2, как обычно со строками (для этого существует специальная предопределенная (в спецификации C#) перегрузка operator ==).   -  person Jeppe Stig Nielsen    schedule 13.09.2018


Ответы (1)


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

[TestFixture]
public class MoqTests
{
    [Test]
    public void MoqOutParameter()
    {
        // Arrange
        Mock<ISample> mockSample = new Mock<ISample>();

        Key MyKey = new Key(DateTime.Today, "SomeValue");
        SampleOutput sampleOut = new SampleOutput() { prop2 = 2 };

        mockSample.Setup(s => s.SampleMethod(It.Is<Key>(t => t.Equals(MyKey)),
            out sampleOut)).Returns(true);

        // Act  
        SampleOutput out1;
        var result1 = mockSample.Object.SampleMethod(new Key(DateTime.Today, "SomeValue"), out out1);

        SampleOutput out2;
        var result2 = mockSample.Object.SampleMethod(new Key(DateTime.MinValue, "AnotherValue"), out out2);

        // Assert
        Assert.True(result1);
        Assert.AreEqual(out1, sampleOut);

        Assert.False(result2);
        Assert.Null(out2);
    }
}

public class Key
{
    public readonly DateTime prop1;
    public readonly string prop2;

    public Key(DateTime prop1, string prop2)
    {
        this.prop1 = prop1;
        this.prop2 = prop2;
    }
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        if (ReferenceEquals(this, obj))
            return true;

        Key other = obj as Key;

        // was forced to add `== 0` to make it compilable
        return this.prop1 == other.prop1 && string.Compare(this.prop2, other.prop2) == 0;
    }

    public override int GetHashCode()
    {
        return prop1.GetHashCode() ^ prop2.GetHashCode();
    }
}

public class SampleOutput
{
    public int prop2 { get; set; }
}

public interface ISample
{
    bool SampleMethod(Key key, out SampleOutput sampleOut);
}

UPD: Я добавил классы Key, SampleOutput и интерфейс 'ISample', которые я использовал в этом примере, а также окружающий класс тестов. Я использую Nunit для запуска теста, это не должно иметь смысла. Вы можете использовать любую структуру модульного тестирования, которую хотите. Этот пример работает для меня, тест зеленый. Пожалуйста, попробуйте и скажите, что отличается от вашей среды. Также обратите внимание, что я изменил строку возврата в Key.Equals, чтобы сделать ее компилируемой.

Надеюсь, поможет.

person KozhevnikovDmitry    schedule 08.09.2018
comment
Спасибо! Я пробовал это, однако этот метод всегда возвращает sampleOut, хотя (Prop1 != 1) в ключевом объекте, а ключевой объект является совершенно новым объектом. Кроме того, для ключевого объекта переопределяются методы equals и hashcode. - person Jack Sparrow; 10.09.2018
comment
В этом случае тест должен провалиться. Имеет ли это? - person KozhevnikovDmitry; 10.09.2018
comment
Ага, понятно. Можете ли вы предоставить ключевой класс объекта? - person KozhevnikovDmitry; 10.09.2018
comment
Да, тесты действительно проваливаются! Добавлен код ключа в вопросе - person Jack Sparrow; 10.09.2018
comment
Представляет ли класс Key SampleClass или SampleKey из исходного фрагмента кода? - person KozhevnikovDmitry; 10.09.2018
comment
он представляет SampleClass - person Jack Sparrow; 10.09.2018
comment
Эй, я обновил пример, пожалуйста, попробуйте и оставьте отзыв =) - person KozhevnikovDmitry; 10.09.2018
comment
Спасибо @КожевниковДмитрий! Оказалось, что с помощью MSTest мне пришлось добавить условие отрицания для настройки вызова фиктивного метода - SampleOut sampleOut = null; mockSample.Setup(s => s.SampleMethod(It.Is‹Key›(t =› (! t.Equals(MyKey))), out sampleOut)).Returns(true); -- Только тогда эти сценарии, похоже, сработают. - person Jack Sparrow; 11.09.2018
comment
Добра тебе=)) - person KozhevnikovDmitry; 11.09.2018