2009-07-13 4 views
7

Возможные Дубликаты:
What is unit testing and how do you do it?
What is unit testing?Что такое единичный тест?

Я признаю, что 95% из вас, это очень WTF вопрос.

So. Что такое единичный тест? Я понимаю, что, по сути, вы пытаетесь изолировать атомную функциональность, а - как вы оцениваете это? Когда это необходимо? Когда это смешно? Можете ли вы привести пример? (Предпочтительно, в C? Я в основном слышал об этом от разработчиков Java на этом сайте, так что, возможно, это специфично для объектно-ориентированных языков? Я действительно не знаю.)

Я знаю, что многие программисты клянутся модульным тестированием религиозно. В чем дело?

EDIT: Также, какое отношение времени вы обычно тратите на проведение блок-тестов на время, потраченное на создание нового кода?

+0

Дубликат дубликатов. Просто найдите переполнение стека для «Что такое единичный тест», и вам будет достаточно, чтобы пережевывать в течение нескольких месяцев. – womp

+0

@womp: Я только что сделал, и я не видел никаких похожих заголовков. –

+2

http://stackoverflow.com/search?q=unit+testing – Nifle

ответ

9

Я сейчас Java, до этого C++, до этого C. Я полностью убежден, что каждая работа, которую я сделал, что я сейчас не соприкасаюсь, была усилена стратегиями тестирования, которые я выбрал. Уход за тестированием болит.

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

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

Мое мнение:

1). Тесты, которые могут быть легко повторены, очень полезны - не поймайте конца поздних ползучих дефектов. В отличие от тестирования, сидящего в отладчике, он ошеломляет.

2). Деятельность по созданию интересных тестов при написании кода или перед написанием кода заставляет вас сосредоточиться на своих случаях. Эти раздражающие нулевые и нулевые входы, которые «отключены на одну ошибку». Я считаю, что лучший код выходит из-за хороших модульных тестов.

3). Существует стоимость поддержания тестов. Как правило, это того стоит, но не стоит недооценивать эффект их работы.

4). Может наблюдаться тенденция к чрезмерному выделению единиц измерения. Действительно интересные ошибки, как правило, ползут, когда части интегрированы. Вы заменяете эту библиотеку, которую вы издеваетесь над настоящей вещью, и Ло! Это не совсем то, что он сказал на олове. Кроме того, все еще есть роль для ручного или эксплуатационного тестирования.Проницательный человеческий тестер находит особые недостатки.

+0

Ничего не сказал. Возможно, вы также можете добавить «как вы сейчас тестируете»: вы просто добавляете (слишком много) протоколирование и просматриваете его при повторном запуске своего приложения, возможно, каждый раз даже щелкаете/просматриваете далеко через ваше приложение, настраиваете правильные данные интерактивно, прежде чем вы увидите, если он работает так, как вы ожидали. (утверждают ожидаемые фактические :-)) – raoulsson

0

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

http://en.wikipedia.org/wiki/Unit_testing

+0

Это довольно грубо. Конечно, я просмотрел статью в википедии об модульном тестировании. На заданные мной вопросы не отвечают. Я бы хотел привести примеры из реального мира. Я понимаю, что это довольно много, и я хотел бы знать, как это используется на практике. – Tyler

+0

Вы также читали внешние ссылки из этой статьи? – Nifle

+0

@Nifle Да. Опять же, нет реального понимания мира. Просто теория. – Tyler

0

Например, если у вас есть класс матрицы, вы можете иметь модульный тест проверки, что

Матрица A = Matrix (.....); A.inverse() * A == Matrix :: Identity

1

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

Я мог бы написать программу калькулятора, которая выглядит красиво, имеет кнопки, выглядит как калькулятор TI-any, и он может производить 2 + 2 = 5. Выглядит неплохо, но вместо того, чтобы отправлять каждую итерацию какого-либо кода человеческому тестеру с длинным списком проверок, я, разработчик, может запускать некоторые автоматические, закодированные модульные тесты на моем коде.

В принципе, единичный тест должен быть испытан сам, сверстниками или другим тщательным обзором, чтобы ответить «это тестирование, что я хочу?»

Единичный тест будет иметь набор «Гивенс» или «Входы» и сравнить их с ожидаемыми «выходами».

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

Стандартной грамматикой для единичного теста может быть строка кода, которая выглядит следующим образом: Assert.AreEqual(a, b).

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

HelloWorldExample helloWorld = new HelloWorldExample(); 
string expected = "Hello World!"; 
string actual = helloWorld.GetString(); 

Assert.AreEqual(expected, actual); 

Если аппарат тест написан на языке конкретной базы (например JUnit, NUnit, и т.д.), результаты каждого метода, который помечен как часть «испытание» будут агрегированные в набор результатов тестов, таких как симпатичный график красных точек для отказа и зеленых точек для успехов и/или XML-файл и т. д.

В ответ на ваши последние комментарии «Теория» может предоставить некоторые реальный мир понимание. TDD, Test Driven Development, много говорит о том, когда и как часто использовать тесты. В моем последнем проекте мы не придерживались TDD, но мы, конечно, использовали модульные тесты, чтобы убедиться, что наш код сделал то, что он должен был делать.

Предположим, вы выбрали интерфейс автомобиля. Интерфейс автомобиля выглядит следующим образом:

interface ICar 
{ 
    public void Accelerate(int delta); 
    public void Decelerate(int delta); 
    public int GetCurrentSpeed(); 
} 

Вы хотите реализовать интерфейс автомобилей в классе FordTaurus:

class FordTaurus : ICar 
{ 
    private int mySpeed; 
    public Accelerate(int delta) 
    { 
     mySpeed += delta; 
    } 
    public Decelerate(int delta) 
    { 
     mySpeed += delta; 
    } 
    public int GetCurrentSpeed() 
    { 
     return mySpeed; 
    } 
} 

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

public static void TestAcceleration(ICar car) 
{ 
    int oldSpeed = car.GetCurrentSpeed(); 
    car.Accelerate(5); 
    int newSpeed = car.GetCurrentSpeed(); 
    Assert.IsTrue(newSpeed > oldSpeed); 
} 
public static void TestDeceleration(ICar car) 
{ 
    int oldSpeed = car.GetCurrentSpeed(); 
    car.Decelerate(5); 
    int newSpeed = car.GetCurrentSpeed(); 
    Assert.IsTrue(newSpeed < oldSpeed); 
} 

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

+0

Выполняете ли модульные тесты в своем коде? Для каждого возможного параметра для каждой функции, которую вы пишете? Каково соотношение времени, затрачиваемого на тестирование модульных тестов, на время, затрачиваемое на написание нового кода? – Tyler

+1

Короткий ответ: это зависит. Проверьте другие ответы SO на этот самый вопрос. Если мне это не нужно, И я не хочу, я этого не делаю. Иногда мне нужно. Иногда я чувствую, что это будет полезно. Другие времена, было бы смешно, если бы я это сделал. Многие люди скажут вам, что если ваш код должен быть передан или повторно принят или когда-либо реорганизован, может быть проведен набор тестов. – maxwellb

+1

Отвечая на вопрос о соотношении времени, затрачиваемого на написание тестов на создание нового кода, следует принять конкретную методологию написания указанного кода. Если вы учитесь в старшей школе и просто хотите получить кодирование опыта, поймите, что для принятия какой-либо методологии требуется время и планирование. Если вы хотите попрактиковаться в TDD, получите книгу по этому предмету и выполните главу за главой, скажем один раз в неделю (примечание: у меня нет специальных знаний о том, какие книги существуют на эту тему). Спросите себя: «Какова моя цель?». Если вы прокладываете себе путь через определенный API, углубление в тесты может быть не ответом. :-) – maxwellb

1

По пунктам:

1) Что такое единичный тест?

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

2) Я понимаю, что по существу вы пытаетесь изолировать атомную функциональность, но как вы ее проверяете?

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

3) Когда это необходимо?

Существует много разных мнений по этому вопросу. Некоторые говорят, что это всегда необходимо, некоторые говорят, что это совершенно не нужно. Я утверждаю, что большинство разработчиков, имеющих опыт работы с Unit Testing, скажут, что Unit Tests необходимы для любого критического кода пути, который имеет дизайн, пригодный для тестирования модулей (я знаю, что это немного круговое, но см. № 2 выше).

  1. Когда это смешно? Можете ли вы привести пример?

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

  1. Я в основном слышал об этом от разработчиков Java на этом сайте, так что, возможно, это относится к объектно-ориентированным языкам?

Нет, это действительно применимо к любому программному обеспечению. Методика Unit Test достигла зрелости с помощью среды Java, но она действительно применима к любому языку или среде.

  1. В чем дело?

Тестирование устройства на самом базовом уровне - все о проверке и проверке того, что поведение, ожидаемое от единицы кода, является НАСТОЯТЕЛЬНО, что делает код.

2

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

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

Так что, взяв этот пример, у меня был вызов класса InterestCalculation, и у него были свойства для всех аргументов и одного общедоступного метода. Calculate() Там, где несколько шагов к вычислению, и если я попытаюсь написать все это в одном метод и просто проверить мой результат было бы подавляющим, чтобы попытаться найти, где моя ошибка /, где ... Итак, я сделал каждый шаг расчета и создал частный метод и единичный тест для всех разных случаев. (Некоторые люди скажут вам только проверить открытые методы, но в этом случае он работал лучше для меня ...) Одним из примеров частных методов были:

Метод:

/// <summary> 
    /// 
    /// </summary> 
    /// <param name="effectiveDate"></param> 
    /// <param name="lastCouponDate"></param> 
    /// <returns></returns> 
    private Int32 CalculateNumberDaysSinceLastCouponDate(DateTime effectiveDate, DateTime lastCouponDate) 
    { 
     Int32 result = 0; 

     if (lastCouponDate.Month == effectiveDate.Month) 
     { 
      result = this._Parameters.DayCount.GetDayOfMonth(effectiveDate) - lastCouponDate.Day; 
     } 
     else 
     { 
      result = this._Parameters.DayCount.GetNumberOfDaysInMonth(lastCouponDate) 
       - lastCouponDate.Day + effectiveDate.Day; 
     } 

     return result; 
    } 

Test Методы:

Примечание: Я бы назвал их по-другому, вместо номера я бы в основном положить резюме в способ name.

/// <summary> 
    ///A test for CalculateNumberDaysSinceLastCouponDate 
    ///</summary> 
    [TestMethod()] 
    [DeploymentItem("WATrust.CAPS.DataAccess.dll")] 
    public void CalculateNumberDaysSinceLastCouponDateTest1() 
    { 
     AccruedInterestCalculationMonthly_Accessor target = new AccruedInterestCalculationMonthly_Accessor(); 
     target._Parameters = new AccruedInterestCalculationMonthlyParameters(); 
     target._Parameters.DayCount = new DayCount(13); 
     DateTime effectiveDate = DateTime.Parse("04/22/2008"); 
     DateTime lastCouponDate = DateTime.Parse("04/15/2008"); 
     int expected = 7; 
     int actual; 

     actual = target.CalculateNumberDaysSinceLastCouponDate(effectiveDate, lastCouponDate); 

     Assert.AreEqual(expected, actual); 

     WriteToConsole(expected, actual); 
    } 

    /// <summary> 
    ///A test for CalculateNumberDaysSinceLastCouponDate 
    ///</summary> 
    [TestMethod()] 
    [DeploymentItem("WATrust.CAPS.DataAccess.dll")] 
    public void CalculateNumberDaysSinceLastCouponDateTest2() 
    { 
     AccruedInterestCalculationMonthly_Accessor target = new AccruedInterestCalculationMonthly_Accessor(); 
     target._Parameters = new AccruedInterestCalculationMonthlyParameters(); 
     target._Parameters.DayCount = new DayCount((Int32) 
      DayCount.DayCountTypes.ThirtyOverThreeSixty); 

     DateTime effectiveDate = DateTime.Parse("04/10/2008"); 
     DateTime lastCouponDate = DateTime.Parse("03/15/2008"); 
     int expected = 25; 
     int actual; 

     actual = target.CalculateNumberDaysSinceLastCouponDate(effectiveDate, lastCouponDate); 

     Assert.AreEqual(expected, actual); 

     WriteToConsole(expected, actual); 
    }    

Где это Смешной?

Ну каждый из них ... Чем больше вы это делаете, тем лучше, когда это будет полезно, и где это кажется «смешным», но лично я не использую его, чтобы проверить мою базу данных таким образом большинство тестировщиков жестких дисков ... В том смысле, что у меня есть сценарии для восстановления схемы базы данных, повторно заполняйте базу данных тестовыми данными и т. д. Обычно я пишу метод единичного теста для вызова метода DataAccess и назовите его суффиксом Debug например: FindLoanNotes_Debug(), и я добавляю System.Diagnostics.Debugger.Break(), поэтому, если я запустил их в режиме отладки, я могу вручную проверить мои результаты.

1

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

Ввод: Файл, написанный на ILOC, языке псевдоассоциирования, который был составлен для моего учебника. Инструкции в файле имеют имена регистров типа «r <number>». Проблема заключается в том, что в программе используется столько регистров, сколько требуется, что обычно больше, чем количество регистров на целевой машине.

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

Чтобы написать эту программу, мне пришлось создать класс, который мог бы анализировать файл ILOC. Я написал кучу тестов для этого класса. Ниже приведены мои тесты (на самом деле у меня было больше, но они избавились от них, чтобы помочь сократить это. Я также добавил некоторые комментарии, чтобы помочь вам прочитать его). Я сделал проект на C++, поэтому я использовал тестовую среду Google C++ (googletest), расположенную here.

Прежде чем показывать вам код ... позвольте мне сказать кое-что о базовой структуре. По существу, есть тестовый класс. Вы можете добавить кучу общих настроек в тестовый класс. Затем есть тестовые макросы, называемые TEST_F. Рамки тестирования подхватывают их и понимают, что их нужно запускать в качестве тестов. Каждый TEST_F имеет 2 аргумента, имя тестового класса и имя теста (который должен быть очень описательным ... таким образом, если тест не удался, вы точно знаете, что не удалось).Вы увидите, что структура каждого теста схожа: (1) настроить некоторые исходные данные, (2) запустить метод, который вы тестируете, (3) проверить правильность вывода. То, как вы проверяете (3), - это использовать макросы, такие как EXPECT_ *. EXPECT_EQ(expected, result) проверяет, что result равно expected. Если это не так, вы получите полезное сообщение об ошибке вроде «результат был бла, но ожидаемый Бла».

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

// Unit tests for the iloc_parser.{h, cc} 

#include <fstream> 
#include <iostream> 
#include <gtest/gtest.h> 
#include <sstream> 
#include <string> 
#include <vector> 

#include "iloc_parser.h" 

using namespace std; 

namespace compilers { 
// Here is my test class 
class IlocParserTest : public testing::Test { 
protected: 
    IlocParserTest() {} 
    virtual ~IlocParserTest() {} 

    virtual void SetUp() { 
    const testing::TestInfo* const test_info = 
     testing::UnitTest::GetInstance()->current_test_info(); 
    test_name_ = test_info->name(); 
    } 

    string test_name_; 
}; 

// Here is a utility function to help me test 
static void ReadFileAsString(const string& filename, string* output) { 
    ifstream in_file(filename.c_str()); 
    stringstream result(""); 
    string temp; 
    while (getline(in_file, temp)) { 
    result << temp << endl; 
    } 
    *output = result.str(); 
} 

// All of these TEST_F things are macros that are part of the test framework I used. 
// Just think of them as test functions. The argument is the name of the test class. 
// The second one is the name of the test (A descriptive name so you know what it is 
// testing). 
TEST_F(IlocParserTest, ReplaceSingleInstanceOfSingleCharWithEmptyString) { 
    string to_replace = "blah,blah"; 
    string to_find = ","; 
    string replace_with = ""; 
    IlocParser::FindAndReplace(to_find, replace_with, &to_replace); 
    EXPECT_EQ("blahblah", to_replace); 
} 

TEST_F(IlocParserTest, ReplaceMultipleInstancesOfSingleCharWithEmptyString) { 
    string to_replace = "blah,blah,blah"; 
    string to_find = ","; 
    string replace_with = ""; 
    IlocParser::FindAndReplace(to_find, replace_with, &to_replace); 
    EXPECT_EQ("blahblahblah", to_replace); 
} 

TEST_F(IlocParserTest, 
     ReplaceMultipleInstancesOfMultipleCharsWithEmptyString) { 
    string to_replace = "blah=>blah=>blah"; 
    string to_find = "=>"; 
    string replace_with = ""; 
    IlocParser::FindAndReplace(to_find, replace_with, &to_replace); 
    EXPECT_EQ("blahblahblah", to_replace); 
} 

// This test was suppsoed to strip out the "r" from register 
// register names in the ILOC code. 
TEST_F(IlocParserTest, StripIlocLineLoadI) { 
    string iloc_line = "loadI\t1028\t=> r11"; 
    IlocParser::StripIlocLine(&iloc_line); 
    EXPECT_EQ("loadI\t1028\t 11", iloc_line); 
} 

// Here I make sure stripping the line works when it has a comment 
TEST_F(IlocParserTest, StripIlocLineSubWithComment) { 
    string iloc_line = "sub\tr12, r10\t=> r13 // Subtract r10 from r12\n"; 
    IlocParser::StripIlocLine(&iloc_line); 
    EXPECT_EQ("sub\t12 10\t 13 ", iloc_line); 
} 


// Here I make sure I can break a line up into the tokens I wanted. 
TEST_F(IlocParserTest, TokenizeIlocLineNormalInstruction) { 
    string iloc_line = "sub\t12 10\t 13\n"; // already stripped 
    vector<string> tokens; 
    IlocParser::TokenizeIlocLine(iloc_line, &tokens); 
    EXPECT_EQ(4, tokens.size()); 
    EXPECT_EQ("sub", tokens[0]); 
    EXPECT_EQ("12", tokens[1]); 
    EXPECT_EQ("10", tokens[2]); 
    EXPECT_EQ("13", tokens[3]); 
} 


// Here I make sure I can create an instruction from the tokens 
TEST_F(IlocParserTest, CreateIlocInstructionLoadI) { 
    vector<string> tokens; 
    tokens.push_back("loadI"); 
    tokens.push_back("1"); 
    tokens.push_back("5"); 
    IlocInstruction instruction(IlocInstruction::NONE); 
    EXPECT_TRUE(IlocParser::CreateIlocInstruction(tokens, 
               &instruction)); 
    EXPECT_EQ(IlocInstruction::LOADI, instruction.op_code()); 
    EXPECT_EQ(2, instruction.num_operands()); 
    IlocInstruction::OperandList::const_iterator it = instruction.begin(); 
    EXPECT_EQ(1, *it); 
    ++it; 
    EXPECT_EQ(5, *it); 
} 

// Making sure the CreateIlocInstruction() method fails when it should. 
TEST_F(IlocParserTest, CreateIlocInstructionFromMisspelledOp) { 
    vector<string> tokens; 
    tokens.push_back("ADD"); 
    tokens.push_back("1"); 
    tokens.push_back("5"); 
    tokens.push_back("2"); 
    IlocInstruction instruction(IlocInstruction::NONE); 
    EXPECT_FALSE(IlocParser::CreateIlocInstruction(tokens, 
              &instruction)); 
    EXPECT_EQ(0, instruction.num_operands()); 
} 

// Make sure creating an empty instruction works because there 
// were times when I would actually have an empty tokens vector. 
TEST_F(IlocParserTest, CreateIlocInstructionFromNoTokens) { 
    // Empty, which happens from a line that is a comment. 
    vector<string> tokens; 
    IlocInstruction instruction(IlocInstruction::NONE); 
    EXPECT_TRUE(IlocParser::CreateIlocInstruction(tokens, 
               &instruction)); 
    EXPECT_EQ(IlocInstruction::NONE, instruction.op_code()); 
    EXPECT_EQ(0, instruction.num_operands()); 
} 

// This was a function that helped me generate actual code 
// that I could output as a line in my output file. 
TEST_F(IlocParserTest, MakeIlocLineFromInstructionAddI) { 
    IlocInstruction instruction(IlocInstruction::ADDI); 
    vector<int> operands; 
    operands.push_back(1); 
    operands.push_back(2); 
    operands.push_back(3); 
    instruction.CopyOperandsFrom(operands); 
    string output; 
    EXPECT_TRUE(IlocParser::MakeIlocLineFromInstruction(instruction, &output)); 
    EXPECT_EQ("addI r1, 2 => r3", output); 
} 

// This test actually glued a bunch of stuff together. It actually 
// read an input file (that was the name of the test) and parsed it 
// I then checked that it parsed it correctly. 
TEST_F(IlocParserTest, ParseIlocFileSimple) { 
    IlocParser parser; 
    vector<IlocInstruction*> lines; 
    EXPECT_TRUE(parser.ParseIlocFile(test_name_, &lines)); 
    EXPECT_EQ(2, lines.size()); 

    // Check first line 
    EXPECT_EQ(IlocInstruction::ADD, lines[0]->op_code()); 
    EXPECT_EQ(3, lines[0]->num_operands()); 
    IlocInstruction::OperandList::const_iterator operand = lines[0]->begin(); 
    EXPECT_EQ(1, *operand); 
    ++operand; 
    EXPECT_EQ(2, *operand); 
    ++operand; 
    EXPECT_EQ(3, *operand); 

    // Check second line 
    EXPECT_EQ(IlocInstruction::LOADI, lines[1]->op_code()); 
    EXPECT_EQ(2, lines[1]->num_operands()); 
    operand = lines[1]->begin(); 
    EXPECT_EQ(5, *operand); 
    ++operand; 
    EXPECT_EQ(10, *operand); 

    // Deallocate memory 
    for (vector<IlocInstruction*>::iterator it = lines.begin(); 
     it != lines.end(); 
     ++it) { 
    delete *it; 
    } 
} 

// This test made sure I generated an output file correctly. 
// I built the file as an in memory representation, and then 
// output it. I had a "golden file" that was supposed to represent 
// the correct output. I compare my output to the golden file to 
// make sure it was correct. 
TEST_F(IlocParserTest, WriteIlocFileSimple) { 
    // Setup instructions 
    IlocInstruction instruction1(IlocInstruction::ADD); 
    vector<int> operands; 
    operands.push_back(1); 
    operands.push_back(2); 
    operands.push_back(3); 
    instruction1.CopyOperandsFrom(operands); 
    operands.clear(); 
    IlocInstruction instruction2(IlocInstruction::LOADI); 
    operands.push_back(17); 
    operands.push_back(10); 
    instruction2.CopyOperandsFrom(operands); 
    operands.clear(); 
    IlocInstruction instruction3(IlocInstruction::OUTPUT); 
    operands.push_back(1024); 
    instruction3.CopyOperandsFrom(operands); 

    // Propogate lines with the instructions 
    vector<IlocInstruction*> lines; 
    lines.push_back(&instruction1); 
    lines.push_back(&instruction2); 
    lines.push_back(&instruction3); 

    // Write out the file 
    string out_filename = test_name_ + "_output"; 
    string golden_filename = test_name_ + "_golden"; 
    IlocParser parser; 
    EXPECT_TRUE(parser.WriteIlocFile(out_filename, lines)); 

    // Read back output file and verify contents are as expected. 
    string golden_file; 
    string out_file; 
    ReadFileAsString(golden_filename, &golden_file); 
    ReadFileAsString(out_filename, &out_file); 
    EXPECT_EQ(golden_file, out_file); 
} 
} // namespace compilers 

int main(int argc, char** argv) { 
    // Boiler plate, test initialization 
    testing::InitGoogleTest(&argc, argv); 
    return RUN_ALL_TESTS(); 
} 

В конце концов, сказано и сделано ... ПОЧЕМУ Я СДЕЛАЛ ЭТО !? Ну в первую очередь. Я написал тесты постепенно, поскольку я готов написать каждый фрагмент кода. Это помогло мне успокоиться, что код, который я уже писал, работает нормально. Было бы безумным написать весь мой код, а затем просто попробовать его в файле и посмотреть, что произошло. Было так много слоев, как я мог знать, откуда возникла ошибка, если бы у меня не было каждой частичной штуки в изоляции?

НО ... САМЫЙ ВАЖНО !!! Тестирование на самом деле не связано с поиском исходных ошибок в вашем коде ... это защита от случайного нарушения вашего кода. Каждый раз, когда я реорганизовал или изменил свой класс IlocParser, я был уверен, что я не изменил его плохо, потому что я мог запускать свои тесты (в считанные секунды) и видеть, что весь код все еще работает, как ожидалось. Это большое использование модульных тестов.

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

+0

Ничего себе. Спасибо, что собрали это вместе. Это было очень, очень полезно. – Tyler