¬ведение в юнит тестирование в VS2005

јвтор. —ергей ћартыненко.
јвторы кода. ћихаил ѕроценко, —ергей ћартыненко.

1. ¬ведение

1.1.†ќбзор

—тать€ написана в стиле Ђƒавайте начнемї. Ќа простом примере рассматриваетс€ модульное тестирование в среде VisualStudio 2005 Team Suite.
Ќикакой глубины или широты обзора не предполагаетс€. ¬ данной статье не рассматриваетс€ стратеги€ Ђ–азработки ¬едомой “естированиемї, стратеги€ тестировани€, подходы к написанию тестов.
¬ стороне остались такие интересные вещи как:
1.††† «апрет публикации не оттестированного кода
2.††† «апись скриптов дл€ веб тестировани€
3.††† ”правление ручными тестами
4.††† Ќагрузочное тестирование
» многое другое.

1.2.»сходна€ задача

“ребуетс€ создать библиотеку дл€ вычислени€ интегралов.
Ѕиблиотека должна вычисл€ть:
Х††† ѕростой факториал
Х††† ƒвойной факториал
Х††† ѕримориал (в первой версии не реализуетс€)
Ѕиблиотека должна выдавать исключение, в случае выхода за пределы допустимого диапазона.
Ѕиблиотека должна вычисл€ть 0! и 0!!
Ѕиблиотека должна возвращать длинное целое.
Ѕиблиотека должна возвращать значение двойной точности, с плавающей зап€той в случае больших целых. ¬ этом случае вычисление проводитс€ по формуле —тирлинга. (в первой версии не реализуетс€)
ѕравила вычислени€ согласно википедии.

1.3.—редство разработки

ƒл€ работы нам понадобитс€ VisualStudio 2005 Team Suite.
VisualStudio 2005 Express Edition или VisualStudio 2005 Professional не поддерживает модульное тестирование.

1.4.ѕроект

“естируемы проект MathLib. »сходный код в приложении. ѕочему именно так, а не по другому в данной статье не рассматриваетс€.
ƒанный код разрабатывалс€ с использованием TDD, но сейчас мы пойдем другим путем. Ѕудем считать, что код уже есть и его надо оттестировать.

2.ѕодготовка к тестированию

2.1.—оздание проекта тестировани€ в солюшене

File -> New -> Project

—оздание тестового проекта. Ўаг 1.
Ќе забудьте указать им€ проекта (согласно соглашению о наименовани€х, в качестве имени тестового проекта берут им€ тестируемого проекта и добавл€ют префикс ЂTestї) и выбрать опцию Ђƒобавить в проектї.

—оздание тестового проекта. Ўаг два.
Ѕудет создан проект тестировани€ с заготовками тестов.
ѕроект тестировани€ можно создать другими способами (см. документацию). Ќо € всегда следовал простой логике: сначала создаем контейнер, потом наполнение.

2.2.—оздание ссылки на тестовый проект

1.††† Solution Explorer -> TestMathLib -> контекстное меню -> Add Reference
2.††† Projects
3.††† ¬ыбрать проект MathLib
ƒобавление ссылки на тестируемый проект.

3. “естирование

ѕеред нами встают вопросы:
Х†††  ак организовать тесты: Ђ аждый в своем файлеї, Ђ¬се в одномї?
ѕопробуем и тот и другой подходы.

Х††† »спользовать ли ручное тестирование?
”далим ручной тест. ¬ этом проекте мы ничего руками тестировать не будем.

Х†††  ак именовать тесты?
” каждого свой взгл€д на соглашение об именах. я предложу следующий подход: тестовый класс должен содержать указани€ на тестируемую функциональность и класс эквивалентности. “естовый метод должен €вно указывать на класс эквивалентности. »деальный вариант Ц код пон€тен без комментариев.

3.1.ѕервый тест

ѕереименуем заготовку в SimpleFactorial.sc
ƒобавим using MathLib;

”далим Ђ#region Additional test attributesї. Ќам не нужно ничего инициализировать и не нужно ничего удал€ть после проведени€ теста.

ѕроведем тест на равенство:
Х††† 5!==120

 од:

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MathLib;

namespace TestMathLib
{
††† [TestClass]
††† public class SimpleFactorial
††† {
††††††† public SimpleFactorial()
††††††† {
††††††† }

††††††† [TestMethod]
††††††† public void TestSimpleCalc()
††††††† {
††††††††††† IFactorial factorial = FactorialProvider.CreateInstance(FactorialType.Simple);
††††††††††† Assert.AreEqual(120L, factorial.Calculate(5));
††††††† }
††† }
}
ѕо€снени€:
Х††† [TestMethod] Ц ”казание, что это тестовый метод, и он должен отображатьс€ в менеджере тестов.
Х††† Assert.AreEqual() - —равнение возвращаемого результата с эталонным.

3.1.1.†††† ѕрогон теста

«апустим наш тест. Shift+Alt+X. “акже тест можно запустить через панель инструментов или главное меню: Test-> Start Selected Test Project with Debugger .

3.1.2.†††† –езультаты прогона

„ерез некоторое врем€ получаем результат. ”спешно пройден один тест.
–езультат первого тестировани€.

 роме того, мы видим закладку ЂCode Coverage Resultsї (покрытие кода).
ѕокрытие кода первым тестированием.

ѕосмотрим, какие участки кода не были пройдены. ƒвойным щелчком на методе, откроем файл SimpleFactorial.cs
ѕросмотр участков кода, непокрытых тестами.

 расным показаны не покрытые участки кода, розовым Ц частично покрытые. “ак проверка number < 0 была сделана, проверка number > 15 Ц нет.

„исло не пройденных блоков считаетс€ следующим образом:
Х††† if(number < 0 || number > 15) Ц 1 раз
Х††† throw new ArgumentOutOfRangeException(”number”); - 2 раза
Х††† return 1L; - 1 раз

3.2.ѕродолжение тестировани€

“естирование граничных условий простого факториала

3.2.1.†††† Ќовый тест

—оздадим новый тест.
Х††† Solution Explorer -> TestMathLib -> AddЕ -> New testЕ -> Unit Test
Х††† ¬ведем название: SimpleFactorialBoundTest.cs

¬ведем тесты на граничные услови€ (код смотри в приложении).
“.к. мы тестируем выход за границы, одним сравнением полученного результата с эталонным нам не обойтись. ѕри выходе за границы ожидаетс€ не значение, а исключение. ¬оспользуемс€ методом ожидани€ исключени€:
[ExpectedException(typeof(ArgumentOutOfRangeException))]
Ѕольше ничего нового дл€ написани€ тестов нам не понадобитс€.

3.2.2.†††† »спользование менеджера тестов

“естов стало больше, и теперь нам необходимо средство управлени€.
1.††† Test -> Windows -> Test Manager
ѕеред нами все наши тесты. «аметим, что не имеет никакого значени€, в одном файле они созданы или в разных.

—группируем тесты по тестируемой функциональности.
1.††† —оздадим списки дл€ простого и двойного факториала
2.††† ¬ыделим тесты, относ€щиес€ к тестированию простого факториала (сейчас это все тесты).
3.††† ѕеренесем их в нужную папку методом drag&drop.

¬ыполним наши тесты.
1.††† ”кажем тесты, которые мы хотим выполнить. ¬зведем чекбокс ЂList of Testsї
ѕометка тестов, которые необходимо выполнить.

2.††† «апустим тесты Ctrl+Shift+X

–езультат: все тесты дл€ простого факториала прошли, в коде вычислени€ простого факториала не осталось не оттестированных участков.

3.3.ƒополнительные функции

“есты занимают некоторое врем€. ѕри большом количестве тестов есть желание прогон€ть только часть. ќдин из способов Ц группировка по функциональности, другой способ Ц группировка по приоритетам.

¬ыстроим следующую шкалу приоритетов:
Х††† 0 Ц неустановлен (по умолчанию)
Х††† 1- минимальный
Х††† 2 Ц средний
Х††† 3 Ц максимальный
¬ окне свойств, зададим приоритет Ђ3ї дл€ простого теста, Ђ2ї дл€ тестов вне границ и Ђ1ї дл€ прочих. “еперь мы можем запускать в качестве приемочных тестов только тесты с наивысшим приоритетом.
√руппировка тестов по приоритетам.

ќбратите внимание, на изменение кода. [TestMethod] изменилс€ на [Priority(2), TestMethod]. ≈сли кому-то удобней задавать приоритеты сразу в коде, то никто не мешает делать так.

4. «аключение

¬ новой студии по€вились прекрасные средства дл€ модульного тестировани€. ¬се в одном месте, все жизненно необходимые функции есть. Ћегко тестировать, легко отлаживать.  онечно, мне как закоренелому критику некоторые вещи не нрав€тс€ (скорее, пока еще непон€тны), но мне всегда что-нибудь не нравитс€.

ѕриложени€

SimpleFactorial.cs

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MathLib;

namespace TestMathLib
{
†[TestClass]
†public class SimpleFactorial
†{
††public SimpleFactorial()
††{
††}

††[Priority(3), TestMethod]
††public void TestSimpleCalc()
††{
†††IFactorial factorial = FactorialProvider.CreateInstance(FactorialType.Simple);
†††Assert.AreEqual(120L, factorial.Calculate(5));
††}
†}
}
SimpleFactorialBoundTest.cs

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MathLib;

namespace TestMathLib
{
†[TestClass]
†public class SimpleFactorialBoundTest
†{
††public SimpleFactorialBoundTest()
††{
††}
††[Priority(1), TestMethod]
††public void TestBottomBound()
††{
†††IFactorial factorial = FactorialProvider.CreateInstance(FactorialType.Simple);
†††Assert.AreEqual(1L, factorial.Calculate(0));
††}

††[Priority(2), TestMethod]
††[ExpectedException(typeof(ArgumentOutOfRangeException))]
††public void TestBottomBoundOut()
††{
†††IFactorial factorial = FactorialProvider.CreateInstance(FactorialType.Simple);
†††factorial.Calculate(-1);
††}

††[Priority(1), TestMethod]
††public void TestBottomBoundIn()
††{
†††IFactorial factorial = FactorialProvider.CreateInstance(FactorialType.Simple);
†††Assert.AreEqual(1L, factorial.Calculate(1));
††}

††[Priority(1), TestMethod]
††public void TestTopBound()
††{
†††IFactorial factorial = FactorialProvider.CreateInstance(FactorialType.Simple);
†††Assert.AreEqual(1307674368000L, factorial.Calculate(15));
††}

††[Priority(1), TestMethod]
††public void TestTopBoundIn()
††{
†††IFactorial factorial = FactorialProvider.CreateInstance(FactorialType.Simple);
†††Assert.AreEqual(87178291200L, factorial.Calculate(14));
††}

††[Priority(2), TestMethod]
††[ExpectedException(typeof(ArgumentOutOfRangeException))]
††public void TestTopBoundOut()
††{
†††IFactorial factorial = FactorialProvider.CreateInstance(FactorialType.Simple);
†††factorial.Calculate(16);
††}
†}
}
SimpleFactorial.cs

using System;

namespace MathLib
{
†internal class SimpleFactorial : IFactorial
†{
††internal SimpleFactorial()
††{
††}
††long IFactorial.Calculate(int number)
††{
†††if(number < 0 || number > 15)
††††throw new ArgumentOutOfRangeException(”number”);

†††if(number == 0)
††††return 1L;

†††long result = 1;
†††for(int i = 1; i <= number; i++)
†††{
††††result *= i;
†††}
†††return result;
††}
†}
}

OddFactorial.cs

using System;

namespace MathLib
{
†internal class OddFactorial : IFactorial
†{
††internal OddFactorial()
††{
††}
††long IFactorial.Calculate(int number)
††{
††††††††††† if (number < 0 || number > 15)
††††††††††††††† throw new ArgumentOutOfRangeException(”number”);

††††††††††† if (number == 0 || number == 1)
††††††††††††††† return 1L;

††††††††††† long result = 1;
††††††††††† for (int i = number; i > 0; i -= 2)
††††††††††† {
††††††††††††††† result *= i;
††††††††††† }
††††††††††† return result;
††}
†}
}

IFactorial.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace MathLib
{
†///
†/// Calculates factorial from 0 to 25.
†///
†public interface IFactorial
†{
††///
††///
††///
††///

††///
††long Calculate(int number);
†}
}††

FactorialProvider.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace MathLib
{
†public enum FactorialType
†{
††Simple,
††Odd,
††††††† Test
†}



†public static class FactorialProvider
†{
††public static IFactorial CreateInstance(FactorialType type)
††{
†††switch(type)
†††{
††††case FactorialType.Odd :
†††††return new OddFactorial();

††††case FactorialType.Simple:
††††††††††††††††††† return new SimpleFactorial();

††††default :
†††††return new SimpleFactorial();
†††}
††}
†}
}

ќдин комментарий

  1. IT –і–ї—П –±–Є–Ј–љ–µ—Б–∞: it4business.ru » –Ш—Б–њ–Њ–ї—М–Ј–Њ–≤–∞–љ–Є–µ WordPress –≤ –њ–Њ–≤—Б–µ–і–љ–µ–≤–љ–Њ–є –ґ–Є–Ј–љ–Є. написал:

    [Е] ¬ставка картинок. Ёто отдельна€ песн€. »наче как оклейкой обоев через замочную скважину это не назовешь. ≈сли б картинка была одна на двадцать статей, то можно было бы помучатьс€. ј если их восемь на статью? ћыши плачут, но продолжают жрать кактус. ≈сли у вас есть возможность, сделайте себе закачку на ftp сервер, а в вордпрессе давайте линки. [Е]

ќставьте комментарий

¬ы должны войти, чтобы оставить свой комментарий.