Любите ли вы Assert.That так, как его любят некоторые другие или выходу беты NUnit v3 посвящается

    Недавно была выпущена первая бета версия тестового фреймворка NUnit v3. Кроме всего прочего, эта версия реализует параллельное выполнение тестов (практически «из коробки»). Я решил проверить как это работает на одном реальном проекте и обнаружил, что новая версия nunit-а не поддерживает часть используемых вещей предыдущих версий. В частности предлагается вместо аттрибута ExpectedException использовать Assert.Thorws или Assert.That.
    Независимо от релиза этой беты, в одном из проектов начал использовать модель Assert.That вместо всех остальных методов и атрибутов nunit-а.

    Под катом небольшой опыт перевода аттрибута ExpectedException в модель Assert.That.


    Как оказалось, тестовый проект, который я выбрал для перевода под nunit v3. содержит более 100 использований аттрибута ExpectedException. Естественно захотелось как то автоматизировать процесс перехода.

    Интересно, что если раньше аттрибут ExpectedException казался очень удачным, то в последнее время обнаружил несколько проблем:
    Например, в следующем тесте не очень понятно, в какой строчке ожидается исключительная ситуация. Обычно — в последней, но если какой-то предыдущий метод выкинет эту же исключительную ситуацию, то тест будет работать не правильно. В любом случае, есть тут какая-то неопределенность «где все-таки ожидать исключительную ситуацию».
            [Test]
            [ExpectedException(typeof(ArgumentException))]
            public void TestExpectedException()
            {
                foo1();
                foo4();
                foo1();
            }
    


    Еще одна мелочь, которая мешает, это отчеты по «покрытию кода», т.е. запускаешь dotCover, изучаешь отчет и видишь:



    А вот заменишь на Assert.That и совсем другое дело: получаешь 100% покрытие.



    После того как я понял, что менять «руками» это слишком простой способ:), решил написать плагин для решарпера, который помогает переводить конструкции nunit в модель Assert.That.

    И начал я с тех, которые мне нужны для перевода моего тестового проекта.

    Сперва все было довольно просто:
            [Test]
            [ExpectedException]
            public void TestShortExpectedException()
            {
                foo1();
                foo2();
                foo1();
            }
    

    перевел в
            [Test]
            public void TestShortExpectedException()
            {
                foo1();
                Assert.That(foo2, Throws.Exception);
                foo1();
            }
    

    Более сложный пример потребовол использования анонимного метода
            [Test]
            [ExpectedException]
            public void TestExpectedExceptionWithExpressions()
            {
                double i = 2 + getNumber();
            }
    

            [Test]
            public void TestExpectedExceptionWithExpressions()
            {
                Assert.That(() => { double i = 2 + getNumber(); }, Throws.Exception);
            }
    

    Конкретный ожидаемый тип потребовал реализации Throws.TypeOf
            [Test]
            [ExpectedException(typeof(ArgumentException))]
            public void TestExpectedException()
            {
                foo1();
                foo4();
                foo1();
            }
    

            [Test]
            public void TestExpectedException()
            {
                foo1();
                Assert.That(() => { foo4(); }, Throws.TypeOf<ArgumentException>());
                foo1();
            }
    

    Ожидаемый текст сообщения исключительной ситауции (или по-русски «месадж эксепшина») потребовал добавить .And.Message
            [Test]
            [ExpectedException(typeof(NotImplementedException), ExpectedMessage = "customer message")]
            public void TestExpectedExceptionWithCustomerMessage()
            {
                foo4("customer message");
            }
    

            [Test]
            public void TestExpectedExceptionWithCustomerMessage()
            {
                Assert.That(() => { foo4("customer message"); }, Throws.TypeOf<NotImplementedException>().And.Message.EqualTo("customer message"));
            }
    


    Пока еще не все конструкции поддерживаются: например, MathType не будет конвертирован корректно.
            [Test]
            [ExpectedException(typeof(NotImplementedException), ExpectedMessage = "customer message", MatchType = MessageMatch.Contains)]
            public void TestExpectedExceptionWithCustomerMessage()
            {
                foo4("my customer message");
            }
    


    Конвертирование конструкций Assert.IsNullOrEmpty and Assert.IsNotNullOrEmpty реализвал без программирования, а только через Custom Patterns.
    Custom Patterns — фича сильная, но, судя по всему, в случае сложных конструкций не все еще гладко работает.
    Assert — конструкция простая и проблем не было:




    Плагин назвается «NUnit.That.Resharper.Plugin» и его бета версия доступна для скачивания через «Resharper — Manage Extensions».
    Тестировал только на resharper-е версии 8.2.
    Прямо сейчас поддерживается небольшой набор конструкций.

    Визуально работа плагина выглядит так:

    выбираешь на нужной строчке Replace


    и получаешь сконвертированное выражение (аттрибут ExpectedException при этом удаляется)



    Выводы:
    — Assert.That мне показался довольно привлекательной моделью;
    — NUnit v3. пока еще бета (осторожно с документацией!), но можно уже начинать примерять на тестовых проектах и подготавливать реальные;
    — полный цикл (включая тесты и дистрибуцию) написания плагинов для решарпера вещь не такая сложная, как могло казаться, и может применяться для решения не только «общих», но и локальных проблем.

    Хотел бы выразить особую благодарность команде resharper-а (и лично mezastel), которые помогли вникнуть в особенности разработки плагинов. Resharper SDK дает возможность создавать проекты Visual Studio из темплейтов, что сильно облегчает дело.

    Ссылки:
    — проект на гитхабе https://github.com/constructor-igor/NUnit.That.Resharper.Plugin
    — плагин NUnit.That.Resharper.Plugin в галерии https://resharper-plugins.jetbrains.com/packages/NUnit.That.Resharper_v8.Plugin/

    Ссылки на примеры и документацию
    — документация для разработчика resharper-а ReSharper DevGuide;
    — пост "Написать плагин для ReSharper — не так и сложно"
    Agent Mulder plugin for ReSharper
    • +20
    • 12.8k
    • 1
    Share post

    Similar posts

    Comments 1

      +1
      А как новый NUnit в сравнении с XUnit.net?

      Only users with full accounts can post comments. Log in, please.