Как узнать, что тестировать при написании модульных тестов? [закрыто]


127

Используя C #, мне нужен класс, Userкоторый имеет имя пользователя, пароль, активный флаг, имя, фамилию, полное имя и т. Д.

Должны быть методы для аутентификации и сохранения пользователя. Я просто напишу тест по методам? И нужно ли мне вообще беспокоиться о тестировании свойств, поскольку они являются геттерами и сеттерами .Net?


Этот пост поможет сузить более широкий вопрос: earnestengineer.blogspot.com/2018/03/… Вы можете воспользоваться этими рекомендациями, чтобы сфокусировать свой вопрос
Линдси Морсилло,

Помните, что пароли не следует хранить в виде открытого текста.
Мистер Андерсон

Ответы:


131

На мой вопрос: « Начало работы с TDD - проблемы? Решения? Рекомендации? » - много замечательных ответов.

Могу я также порекомендовать взглянуть на мой пост в блоге (который частично был вдохновлен моим вопросом), у меня есть хорошие отзывы по этому поводу. А именно:

Я не знаю, с чего начать?

  • Начни заново. Думайте о написании тестов только тогда, когда пишете новый код. Это может быть переработка старого кода или совершенно новая функция.
  • Начни с простого. Не убегайте и не пытайтесь разобраться в среде тестирования, а также быть в стиле TDD. Debug.Assert работает нормально. Используйте это как отправную точку. Это не влияет на ваш проект и не создает зависимостей.
  • Начни положительно. Вы пытаетесь улучшить свое ремесло, вам это нравится. Я видел множество разработчиков, которые счастливы застаиваться и не пробуют что-то новое, чтобы улучшить себя. Вы поступаете правильно, помните об этом, и это поможет вам не сдаться.
  • Готовьтесь к испытаниям. Приступить к тестированию довольно сложно. Ожидайте трудностей, но помните - проблемы можно преодолеть.

Проверяйте только то, чего вы ожидаете

Когда я только начинал, у меня были настоящие проблемы, потому что я постоянно сидел, пытаясь выяснить все возможные проблемы, которые могли возникнуть, а затем пытался проверить их и исправить. Это быстрый способ избавиться от головной боли. Тестирование должно быть настоящим ЯГНИ-процессом. Если вы знаете, что есть проблема, напишите для нее тест. В противном случае не беспокойтесь.

Только проверить одну вещь

Каждый тестовый пример должен проверять только одно. Если вы когда-нибудь обнаружите, что добавляете «и» в название тестового примера, вы делаете что-то не так.

Надеюсь, это означает, что мы можем перейти от "геттеров и сеттеров" :)


2
«Если вы знаете, что есть проблема, то напишите для нее тест. В противном случае не беспокойтесь». Я бы не согласился с такой формулировкой. У меня создалось впечатление, что модульные тесты должны охватывать все возможные пути выполнения.
Корин Блейки,

3
Хотя некоторые могут выступать за такие вещи, я лично этого не делаю. Добрые 90% моей головной боли возникали из-за того, что я просто пытался делать «все». Я говорю «проверьте» на то, что вы ожидаете произойти (чтобы вы знали, что получаете обратно правильные значения), но не пытайтесь понять все это. YAGNI.
Роб Купер,

4
Я тоже сторонник подхода «проверьте свои ошибки». Если бы у всех нас было бесконечное время и терпение, мы бы протестировали все возможные пути выполнения. Но мы этого не делаем, поэтому вы должны направить свои усилия туда, где это будет иметь наибольший эффект.
Schwern

63

Проверяйте свой код, а не язык.

Модульный тест вроде:

Integer i = new Integer(7);
assert (i.instanceOf(integer));

полезен только в том случае, если вы пишете компилятор и вероятность того, что ваш instanceofметод не работает, отлична от нуля .

Не тестируйте то, что вы можете применить на языке. В вашем случае я бы сосредоточился на ваших методах аутентификации и сохранения - и я бы написал тесты, чтобы убедиться, что они могут корректно обрабатывать нулевые значения в любом или всех этих полях.


1
Хороший момент по поводу «Не тестируйте фреймворк» - кое-что, что я получил, когда впервые в этом был. +1 :)
Роб Купер

38

Это привело меня к модульному тестированию и очень обрадовало

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

Затем нам пришлось переписать важный фрагмент кода нашей бухгалтерской программы. Эта часть была очень сложной, так как предполагала множество различных сценариев. Речь идет о методе оплаты счетов-фактур продаж и / или покупок, уже введенных в систему бухгалтерского учета.

Я просто не знал, с чего начать, так как вариантов оплаты было очень много. Счет-фактура может составлять 100 долларов, но клиент перевел только 99 долларов. Возможно, вы отправили счета-фактуры клиенту, но вы также совершили покупку у этого клиента. Итак, вы продали его за 300 долларов, а купили за 100 долларов. Вы можете ожидать, что ваш клиент заплатит вам 200 долларов для погашения баланса. А что, если вы продали за 500 долларов, а покупатель заплатит вам всего 250 долларов?

Таким образом, у меня была очень сложная проблема, которую нужно было решить, с множеством возможностей, что один сценарий будет работать идеально, но будет неправильным для другого типа комбинации счет / платеж.

Здесь на помощь пришло модульное тестирование.

Я начал писать (внутри тестового кода) метод создания списка счетов-фактур, как для продаж, так и для покупок. Затем я написал второй способ создания фактического платежа. Обычно пользователь вводит эту информацию через пользовательский интерфейс.

Затем я создал первый TestMethod, тестируя очень простую оплату одного счета без каких-либо скидок при оплате. Все действия в системе будут происходить, когда банковский платеж будет сохранен в базе данных. Как видите, я создал счет, создал платеж (банковскую транзакцию) и сохранил транзакцию на диск. В своих утверждениях я указываю правильные числа, которые должны отображаться в банковской транзакции и в связанном счете-фактуре. Я проверяю количество платежей, суммы платежей, размер скидки и остаток счета после транзакции.

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

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

Я провел первый тест, исправил небольшую ошибку, пока мой тест не прошел.

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

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

Затем я перешел к более сложным сценариям.

1) Придумайте новый сценарий

2) Напишите тест для этого сценария

3) Запустите этот единственный тест, чтобы узнать, пройдет ли он

4) В противном случае я бы отлаживал и изменял код, пока он не прошел.

5) При изменении кода я продолжал запускать все тесты

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

Я уверен, что использование модульного тестирования сэкономило мне несколько дней (или недель) программирования и более или менее гарантирует правильность моего метода.

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

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

Ниже приведены лишь некоторые из тестов, которые я создал для проверки моего метода оплаты.

public class TestPayments
{
    InvoiceDiaryHeader invoiceHeader = null;
    InvoiceDiaryDetail invoiceDetail = null;
    BankCashDiaryHeader bankHeader = null;
    BankCashDiaryDetail bankDetail = null;



    public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
    {
        ......
        ......
    }

    public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
    {
       ......
       ......
       ......
    }


    [TestMethod]
    public void TestSingleSalesPaymentNoDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 1, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSingleSalesPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 2, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    [ExpectedException(typeof(ApplicationException))]
    public void TestDuplicateInvoiceNumber()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("100", true, 2, "01-09-2008"));
        list.Add(CreateSales("200", true, 2, "01-09-2008"));

        bankHeader = CreateMultiplePayments(list, 3, 300, 0);
        bankHeader.Save();
        Assert.Fail("expected an ApplicationException");
    }

    [TestMethod]
    public void TestMultipleSalesPaymentWithPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 11, "01-09-2008"));
        list.Add(CreateSales("400", true, 12, "02-09-2008"));
        list.Add(CreateSales("600", true, 13, "03-09-2008"));
        list.Add(CreateSales("25,40", true, 14, "04-09-2008"));

        bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
        Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);

        Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);

        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSettlement()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("300", true, 43, "01-09-2008")); //Sales
        list.Add(CreateSales("100", false, 6453, "02-09-2008")); //Purchase

        bankHeader = CreateMultiplePayments(list, 22, 200, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
    }

1
Нашел ошибку в вашем модульном тесте! Вы повторяете эту строку вместо того, чтобы включать 2 в одну из них:Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
Райан Пешел

2
Вы говорите: «После запуска теста я обращаюсь к базе данных и дважды проверяю, есть ли там то, что я ожидал». Это хороший пример интеграционного теста между компонентами вашей системы, а не изолированного модульного теста одного фрагмента кода.
JTech

2
Вы также нарушили правило более одного утверждения на тест.
Стив

13

Если они действительно тривиальны, не беспокойтесь о тестировании. Например, если они реализованы таким образом;

public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
}

Если, с другой стороны, вы делаете что-то умное (например, шифруете и расшифровываете пароль в геттере / сеттере), то проверьте это.


10

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


6

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

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

Надежность кода, особенно когда в него вносятся изменения для добавления дополнительных функций, является основной целью. Я не знаю, чтобы кого-либо когда-либо увольняли за включение сеттеров / получателей в методологию тестирования, но я уверен, что есть люди, которые хотели бы, чтобы они тестировали методы, которые в последний раз знали или могут вспомнить, были простыми обертками set / get, но это не было дольше дело.

Возможно, другой член команды расширил методы set / get, включив в них логику, которая теперь нуждается в тестировании, но не создавала тесты. Но теперь ваш код вызывает эти методы, и вы не знаете, что они изменились и нуждаются в глубоком тестировании, а тестирование, которое вы проводите при разработке и контроле качества, не вызывает дефект, но реальные бизнес-данные в первый день выпуска вызывают вызвать это.

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

Я считаю, что несколько минут «потраченного впустую» времени, покрывающего ВСЕ методы модульными тестами, даже тривиальными, могут сэкономить несколько дней головной боли в будущем и потерю денег / репутации бизнеса и потерю работы.

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

То, как мы кодируем, и дисциплина, которую мы видим из нашего кода, могут помочь другим.


4

Еще один канонический ответ. Я думаю, это от Рона Джеффриса:

Проверяйте только тот код, с которым хотите работать.


3

Тестирование стандартного кода - пустая трата времени, но, как говорит Славо, если вы добавите побочный эффект к своим геттерам / сеттерам, вам следует написать тест, чтобы сопровождать эту функциональность.

Если вы занимаетесь разработкой, управляемой тестами, вы должны сначала написать контракт (например, интерфейс), а затем написать тест (ы) для проверки этого интерфейса, который документирует ожидаемые результаты / поведение. Затем напишите сами свои методы, не касаясь кода в своих модульных тестах. Наконец, возьмите инструмент покрытия кода и убедитесь, что ваши тесты проверяют все логические пути в вашем коде.


3

Действительно тривиальный код, такой как методы получения и установки, который не имеет дополнительного поведения, кроме установки частного поля, является излишним для тестирования. В 3.0 C # даже есть некоторый синтаксический сахар, когда компилятор заботится о закрытом поле, поэтому вам не нужно его программировать.

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


Рад, что вы хорошо изложили принцип KISS. У меня часто есть тесты, которые буквально похожи на 2-3 строки кода, очень маленькие, простые тесты. Легко грокнуть и трудно сломать :) +1
Роб Купер

3

Вы должны все протестировать. Прямо сейчас у вас есть геттеры и сеттеры, но однажды вы можете их немного изменить, возможно, для проверки или чего-то еще. Тесты, которые вы напишете сегодня, будут использованы завтра, чтобы убедиться, что все работает как обычно. Когда вы пишете тест, вы должны забыть о таких соображениях, как «сейчас это тривиально». В контексте гибкой разработки или тестирования вы должны протестировать, предполагая будущий рефакторинг. Кроме того, вы пробовали вводить действительно странные значения, такие как очень длинные строки или другой «плохой» контент? Что ж, вы должны ... никогда не предполагать, насколько серьезно ваш код может быть использован в будущем.

Обычно я считаю, что написание обширных пользовательских тестов утомительно, с одной стороны. С другой стороны, хотя он всегда дает вам неоценимое представление о том, как должно работать ваше приложение, и помогает отбросить простые (и ложные) предположения (например: имя пользователя всегда будет меньше 1000 символов в длину).


3

Для простых модулей, которые могут оказаться в наборе инструментов или в проекте с открытым исходным кодом, вы должны протестировать как можно больше, включая тривиальные методы получения и установки. Следует иметь в виду, что создание модульного теста при написании конкретного модуля довольно просто и понятно. Добавление геттеров и сеттеров - это минимальный код, с которым можно легко справиться. Однако, как только ваш код будет помещен в более крупную систему, эти дополнительные усилия могут защитить вас от изменений в базовой системе, таких как изменения типа в базовом классе. Тестирование всего - лучший способ добиться полной регрессии.


2

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


Что ж, если вашим геттерам / сеттерам нужны модульные тесты, с ними должна быть связана какая-то логика, так что это означает, что логика должна быть написана внутри них, если у них нет никакой логики, никаких модульных тестов писать не нужно.
Pop Catalin

2
Он считает, что позже к ним можно добавить логику.
LegendLength,

2

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

В иерархиях наследования обязательно проверьте соответствие LSP .

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


2

хорошо, если вы думаете, что он может сломаться, напишите для него тест. Я обычно не тестирую сеттер / получатель, но позволяет вам сделать его для User.Name, который объединяет имя и фамилию, я бы написал тест, чтобы, если кто-то изменит порядок для фамилии и имени, по крайней мере, он будет знать он изменил то, что было проверено.


2

Канонический ответ - «тестируйте все, что может сломаться». Если вы уверены, что свойства не сломаются, не проверяйте их.

И как только обнаруживается, что что-то сломалось (вы обнаруживаете ошибку), это, очевидно, означает, что вам нужно проверить это. Напишите тест, чтобы воспроизвести ошибку, посмотрите, как он не работает, затем исправьте ошибку, затем посмотрите, как проходит тест.


1

Насколько я понимаю модульные тесты в контексте гибкой разработки, Майк, да, вам нужно протестировать геттеры и сеттеры (при условии, что они общедоступны). Вся концепция модульного тестирования заключается в том, чтобы протестировать программный модуль, который в данном случае является классом, как черный ящик . Поскольку геттеры и сеттеры видны извне, вам необходимо протестировать их вместе с Authenticate и Save.


1

Если методы Authenticate и Save используют свойства, тогда ваши тесты будут косвенно касаться свойств. Пока свойства просто предоставляют доступ к данным, явное тестирование не требуется (если вы не собираетесь на 100% покрытие).


1

Я бы протестировал ваши геттеры и сеттеры. В зависимости от того, кто пишет код, некоторые люди меняют значение методов получения / установки. Я видел инициализацию переменных и другие проверки как часть методов получения. Чтобы протестировать такие вещи, вам понадобятся модульные тесты, явно охватывающие этот код.


1

Лично я бы «протестировал все, что может сломаться», и простой геттер (или даже лучше автоматические свойства) не сломается. У меня никогда не было ошибок простого оператора return, и поэтому я никогда не проверял их. Если у геттеров есть вычисления или какие-то другие формы операторов, я бы обязательно добавил для них тесты.

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


1

Вы должны покрыть выполнение каждого метода класса с помощью UT и проверить возвращаемое значение метода. Сюда входят методы получения и установки, особенно в случае, если члены (свойства) являются сложными классами, что требует выделения большого объема памяти во время их инициализации. Вызовите сеттер, например, с какой-нибудь очень большой строкой (или чем-то с греческими символами) и убедитесь, что результат правильный (не усечен, кодировка хорошая и т. Д.)

В случае простых целых чисел это также применимо - что произойдет, если вы передадите long вместо integer? Вот почему вы пишете UT :)


1

Я бы не стал тестировать фактическую настройку свойств. Меня больше беспокоит, как эти свойства заполняются потребителем и чем они их заполняют. При любом тестировании вы должны взвесить риски со временем / стоимостью тестирования.


1

Вы должны тестировать «каждый нетривиальный блок кода», используя модульные тесты, насколько это возможно.

Если ваши свойства тривиальны и маловероятно, что кто-то внесет в них ошибку, то их следует безопасно не тестировать.

Ваши методы Authenticate () и Save () выглядят хорошими кандидатами для тестирования.


1

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

Писать тесты потом гораздо труднее, но выполнимо.

Вот что я бы сделал на вашем месте:

  1. Напишите базовый набор тестов для проверки основной функции.
  2. Получите NCover и запустите его на своих тестах. На данный момент ваше тестовое покрытие, вероятно, составит около 50%.
  3. Продолжайте добавлять тесты, которые охватывают ваши крайние случаи, пока вы не достигнете охвата примерно 80% -90%

Это должно дать вам хороший рабочий набор модульных тестов, которые будут действовать как хороший буфер против регрессий.

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

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


1

Не тестируйте заведомо рабочий (шаблонный) код. Поэтому, если ваши сеттеры и геттеры - это просто «propertyvalue = value» и «return propertyvalue», нет смысла проверять это.


1

Даже get / set могут иметь странные последствия в зависимости от того, как они были реализованы, поэтому их следует рассматривать как методы.

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

Вам также необходимо знать о подводных камнях безопасности, например о SQL-инъекции, и проверять их.

Так что да, вам нужно побеспокоиться о тестировании свойств.


1

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


1

Я бы написал тест для всего, что вы пишете код, который можно тестировать вне графического интерфейса.

Как правило, любую написанную мной логику, имеющую любую бизнес-логику, я помещаю на другой уровень или уровень бизнес-логики.

Тогда написать тесты для всего, что что-то делает, будет легко.

Сначала напишите модульный тест для каждого общедоступного метода на «уровне бизнес-логики».

Если бы у меня был такой класс:

   public class AccountService
    {
        public void DebitAccount(int accountNumber, double amount)
        {

        }

        public void CreditAccount(int accountNumber, double amount)
        {

        }

        public void CloseAccount(int accountNumber)
        {

        }
    }

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

   [TestFixture]
    public class AccountServiceTests
    {
        [Test]
        public void DebitAccountTest()
        {

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

Напишите свои тесты, чтобы проверить код, который вы написали, чтобы что-то сделать. Если вы повторяете набор вещей и что-то меняете в каждой из них, напишите тест, который делает то же самое, и подтвердите, что действительно произошло.

Есть много других подходов, которые вы можете использовать, а именно Behavoir Driven Development (BDD), которые более сложны и не являются отличным местом для начала ваших навыков модульного тестирования.

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

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

Я рекомендую TestDriven.Net или ReSharper, поскольку оба они легко интегрируются в Visual Studio.


1

Я бы рекомендовал написать несколько тестов для ваших методов аутентификации и сохранения. В дополнение к успешному случаю (когда все параметры указаны, все написано правильно и т. Д.), Хорошо иметь тесты на различные случаи отказа (неверные или отсутствующие параметры, недоступные соединения с базой данных, если применимо, и т. Д.). Я рекомендую Pragmatic Unit Testing на C # с NUnit в качестве справочника.

Как заявляли другие, модульные тесты для геттеров и сеттеров излишни, если в ваших геттерах и сеттерах нет условной логики.


1

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

Код с большим количеством тестов, но с небольшим охватом, не был хорошо протестирован. Тем не менее, код со 100% покрытием, но без проверки границ и ошибок тоже не очень хорош.

Вам нужен баланс между высоким охватом (минимум 90%) и переменными входными данными.

Не забудьте проверить "мусор в"!

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

Вам необходимо разработать свои тесты так, чтобы они всегда сообщали о сбоях или неожиданных / нежелательных данных!


1

Это делает наш код лучше ... точка!

Одна вещь, о которой мы, разработчики программного обеспечения, забываем, занимаясь разработкой через тестирование, - это цель наших действий. Если модульный тест пишется после того, как производственный код уже готов, значение теста сильно уменьшается (но не теряется полностью).

В истинном духе модульного тестирования эти тесты предназначены не для того, чтобы «тестировать» большую часть нашего кода; или улучшить покрытие кода на 90% -100%. Это все дополнительные преимущества написания тестов в первую очередь. Большим преимуществом является то, что наш рабочий код будет написан намного лучше благодаря естественному процессу TDD.

Чтобы лучше донести эту идею, при чтении может быть полезно следующее:

Ошибочная теория модульных тестов
Целенаправленная разработка программного обеспечения

Если мы чувствуем, что написание большего количества модульных тестов - это то, что помогает нам получить более качественный продукт, тогда мы, возможно, страдаем от культа разработки, основанной на тестировании.


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

Возможно, я ошибся. Я не имел в виду, что модульные тесты не имеют значения после того, как готов производственный код. Однако их стоимость снижается. Самое большое преимущество модульного тестирования - это магия, которая возникает, когда мы позволяем им управлять нашей производственной разработкой.
Скотт Саад,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.