Autofixture: NSubstitute.ReceivedCalls () возвращает неверное значение с помощью AutoFixture.AutoNSubstitute

Созданный на 28 мар. 2018  ·  10Комментарии  ·  Источник: AutoFixture/AutoFixture

У нас есть много тестов с использованием Class.ReceivedCalls () и var tmp = Class.Received (int) .Property; чтобы проверить количество звонков. В версии 3 AutoFixture он правильно сообщал количество вызовов, особенно свойств, чего он больше не делает. Счетчик вызовов методов, кажется, все еще в порядке.
Учитывая, что у нас есть следующий код:

    public interface IRunSpeed
    {
        int Speed { get; }
    }

    public class GetToDaChoppa
    {
        private readonly IRunSpeed _runSpeed;

        public GetToDaChoppa(IRunSpeed runSpeed)
        {
            _runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
        }

        public void DoItNow()
        {
            var runningSpeed = _runSpeed.Speed;
        }
    }

И учитывая, что у нас есть следующие тесты:

        [Fact]
        public void DoItNow_WithOutAutoNSubstitute()
        {
            // Arrange
            var runSpeed = Substitute.For<IRunSpeed>();
            runSpeed.Speed.Returns(2);

            var sut = new GetToDaChoppa(runSpeed);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Single(runSpeed.ReceivedCalls());
        }

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Single(runSpeed.ReceivedCalls());

Мой AutoNSubstituteDataAttribute выглядит так:

        public class AutoNSubstituteDataAttribute : AutoDataAttribute
        {
            public AutoNSubstituteDataAttribute()
                : base(() => new Fixture()
                           .Customize(new AutoNSubstituteCustomization()))
            {
            }
        }

Первый тест «DoItNow_WithOutAutoNSubstitute» работает нормально. Но второй тест «DoItNow_UsingAutoNSubstitute» возвращает 2 для «ios.Received (1) .Speed;».
Он также возвращает 2 для runSpeed.ReceivedCalls ();.
Из-за этого мы в настоящее время не можем обновить наши Решения до версии 4, так как у нас сразу появляется> 1000 неудачных тестов на одно решение. Есть какие-нибудь указания относительно того, в чем может быть проблема или где искать исправление?

question

Все 10 Комментарий

Спасибо, что подняли вопрос. На самом деле, эта проблема не имеет ничего общего с AutoFixture, хотя и нас она тоже затрагивает 😕

Эта проблема в основном вызвана xUnit, и если вы перепишете тест следующим образом, он пройдет:
`` С #
[Теория, AutoNSubstituteData]
public void DoItNow_UsingAutoNSubstitute_CreateManually (прибор IFixture)
{
// Договариваться
var runSpeed ​​= fixture.Freeze();
var sut = fixture.Create();
runSpeed.Speed.Returns (49);

//Act
sut.DoItNow();

//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Single(runSpeed.ReceivedCalls());

}
`` ''

Когда вы запускаете тесты, xUnit пытается отформатировать имя теста за вас. Если он обнаруживает, что этот тип неизвестен и не имеет перегрузки ToString() , он использует структурную проверку и рекурсивно выбирает значения свойств. Если вы проверите название теста, вы увидите следующее:
image

Как вы могли заметить, xUnit получил значение свойства Speed чтобы красиво показать вам тестовый аргумент. Поскольку это обычный вызов, NSubstitute посчитал это вызовом.

Довольно странно, что у вас не было этой проблемы с v3, поскольку в этом отношении ничего не изменилось. Я только что протестировал xUnit2 + AutoFixture v3 но проблема не исчезла. Возможно, вы тоже обновили xUnit.


Что касается обходного пути, у меня есть хорошие и плохие новости. Хорошей новостью является то, что эта проблема будет решена в следующей основной версии NSubsitute, поскольку он начал переопределять метод ToString() для возврата идентификатора прокси. В результате xUnit больше не трогает свойства:
image

Плохая новость в том, что NSubsitute v4 еще не выпущен.

Я мог бы предложить вам либо:

  • отложить миграцию до выхода NSubsitute v4;
  • используйте последний master исходный код NSubsitute, создайте локальную сборку и используйте свой собственный NSubsitute.dll вместо пакета NuGet. После выпуска версии 4 вы можете переключиться на пакет NuGet.
  • если вы также обновили версию xUnit (поскольку это может объяснить, почему у вас не было этой проблемы раньше), отмените это изменение и используйте предыдущую версию xUnit.

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

только что проверил это. наконец, наши тесты, подтверждающие ReceivedCalls, снова дали успешный результат.
Что-то, что все еще терпит неудачу, - это утверждения на Received (x), такие как Received (1) .Speed ​​Daniel, упомянутый выше.

snip_20180328181559

У вас тоже есть ответ / решение для шапки?

Вы работаете над одним проектом? :) Если да, не могли бы вы пояснить, как вы устранили проблему?

Предложенные выше варианты должны помочь в решении обеих проблем. Если нет - проясните сценарий.

не совсем тот же проект, но в той же компании.
мы были на xunit 1.9 + nsubstitute 2/3 в течение долгого времени и, наконец, смогли получить наши внутренние сборки (пакеты nuget, поэтому простой способ вернуться к более старой версии только одной ссылки не так прост, как кажется быть), чтобы быть совместимым с xunit2. и теперь у нас есть эти две проблемы.

Мы уже опробовали некоторые вещи и, наконец, подумали, что это должно иметь какое-то отношение к автофиксации - из-за замороженного атрибута и жестко запрограммированного счетчика выполнения тестов - или чего-то в этом роде.
После того, как Дэниел открыл это дело, он отправил мне ссылку на него.

так же, как вы описали выше, я заменил ссылку на nuget 3.1 ссылкой из недавно скомпилированного проекта nsubstitute. после этого у меня теперь 118 вместо 178 неудачных тестов, потому что Receiced (x) по-прежнему оценивает неправильное количество вызовов.

мой проект в настоящее время основан на .net 4.5.2 с указанием xunit2, текущей версии репо nsubstitute и autofixture 4.2. все тесты терпят неудачу по одним и тем же двум причинам - также есть некоторые полученные вызовы, которые не проходят в модульных тестах.
Думаю, мне нужно еще раз взглянуть на это глубже, когда я снова вернусь в офис (на пасху).
может быть, @dklinger тем временем может описать сценарий.

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

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

Итак, я проверил последние несколько часов. Во-первых: спасибо за ваш ответ, ваши объяснения и предложения - это очень помогло.
Я тестировал v4 NSubstitute. Это работает для моего примера кода выше, но не решает полностью проблему в наших реальных проектах. Проблема в том, что он решает проблему неправильного счетчика ReceivedCalls только для вызовов SUT к методам, а не к свойствам.

Пример нашего теперь рабочего кода:

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Но если я изменю метод "GetToDaChoppa.DoItNow ();" чтобы быть свойством "GetToDaChoppa.DoItNow", ReceivedCallsCount снова равен +1:

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            var x = sut.DoItNow;

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

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

И этот для нерабочей реализации свойства:
image

Похоже, что xUnit вызывает SUT-свойство, чтобы получить его значение, чтобы использовать его для имени теста, но не делает этого для SUT-методов. Моя первая мысль заключалась в том, что это связано с возвращаемым значением нашего метода GetToDaChoppa (), которое было недействительным. Но даже после изменения его на «public int GetToDaChoppa ()» xUnit по-прежнему не вызывает его для получения возвращаемого значения для имени теста. Проблема только в использовании свойств.

А пока я снова застрял. Я полностью согласен с тем, что это не проблема AutoFixture. Но, по вашему мнению, зная все пакеты лучше, чем мы, что бы вы посоветовали?

  • открываете вопрос для NSubstitute?
  • открыть вопрос для xUnit2?
  • или что-то совершенно новое?

@dklinger Спасибо за продолжение! Не могли бы вы поделиться MCVE кода для последнего сценария, который все еще терпит неудачу? Просто чтобы убедиться, что ничего не упущено, и я не неправильно понял условия.

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

Спасибо.

Ага, конечно. Вот:

Тестируемая система:

    public interface IRunSpeed
    {
        int Speed { get; }
        void Dude();
        int Dude2();
    }

    public class GetToDaChoppa
    {
        private readonly IRunSpeed _runSpeed;

        public GetToDaChoppa(IRunSpeed runSpeed)
        {
            _runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
        }

        public int DoItNow
        {
            get
            {
                var runningSpeed = _runSpeed.Speed;
                return 0;
            }
        }
    }

Прецедент:

        [Theory, AutoNSubstituteData]
        public void DoItNowAsProperty_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            var x = sut.DoItNow;

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

В настоящее время я пытаюсь следить за обсуждениями на https://github.com/xunit/xunit/issues/1386 и https://github.com/AutoFixture/AutoFixture/issues/805. Возможно, мы что-то делаем не так или есть способ запретить xUnit автоматически генерировать имя теста.
Я также попытался создать свой собственный атрибут TheoryAttribute, переопределяющий свойство DisplayName, но это тоже не помогает, поскольку xUnit каким-то образом все еще использует автоматически сгенерированное имя внутри. Но это просто fyi и совершенно не связано с демонстрационным кодом выше.

        public sealed class MyTheoryAttribute : TheoryAttribute
        {
            public MyTheoryAttribute([CallerMemberName] string memberName = null)
            {
                DisplayName = "MyTestCase";
            }
        }

image

Привет, снова я :)
Это действительно xUnit2, вызывающий все свойства и поля параметров тестового метода, которые имеют сложный тип. Строка кода следующая: https://github.com/xunit/assert.xunit/blob/2b70a9b0c5bb291f98472ec24cec437acf8d65c8/Sdk/ArgumentFormatter.cs#L156

Спасибо за поддержку. Я повторно открыл проблему в xUnit-Repo, обсуждение которой должно продолжаться: https://github.com/xunit/xunit/issues/1682

@dklinger Спасибо за подробный сценарий. Это действительно довольно сложно и сложно как-то обойтись. С точки зрения AutoFixture и NSubsitute нет разницы, вызывается ли код где-то глубоко внутри xUnit или в теле теста.

Обычно в качестве обходного пути можно использовать четкую функцию NSubstitute:

runSpeed.ClearReceivedCalls();

Этот код следует запускать в прологе каждого теста, где вы проверяете точное количество вызовов. Он отлично работает, если у вас есть несколько тестов, однако очевидно, что если затронуты тысячи тестов, это не сильно поможет 😅

Жаль, что интеграция NSubsitute + xUnit2 + AutoFixture не работает должным образом и страдает от подобных проблем. Продукт AutoFixture был разработан, чтобы упростить жизнь, а не превратить ее в кошмар 😕 Надеюсь, ребята из xUnit посоветуют вам быстрый способ решения проблемы для всего проекта.

Дайте мне знать, если вы считаете, что мы можем что-то сделать с нашей стороны, чтобы улучшить ситуацию.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги