तो क्या आप उदाहरण चाहते हैं? पिछले सेमेस्टर में मैंने एक कंपाइलर्स कोर्स लिया। इसमें हमें एक रजिस्टर आवंटक लिखना पड़ा। सरल शब्दों में कहें, मेरा कार्यक्रम इस तरह संक्षेप किया जा सकता है:
इनपुट: एक ILOC, एक छद्म विधानसभा भाषा है कि मेरे पाठ्यपुस्तक के लिए बनाया गया था में लिखा फ़ाइल। फ़ाइल में निर्देशों के नाम "आर <number>
" हैं। समस्या यह है कि कार्यक्रम जितना आवश्यक हो उतने रजिस्टरों का उपयोग करता है, जो आमतौर पर लक्ष्य मशीन पर रजिस्टरों की संख्या से अधिक होता है।
आउटपुट: आईएलओसी में लिखी गई एक और फाइल। इस बार, निर्देशों को फिर से लिखा जाता है ताकि यह उन रजिस्टरों की सही अधिकतम संख्या का उपयोग कर सके जो अनुमति है।
इस कार्यक्रम को लिखने के लिए, मुझे एक कक्षा बनाना था जो आईएलओसी फ़ाइल को पार्स कर सकता था। मैंने उस वर्ग के लिए परीक्षणों का एक गुच्छा लिखा था। नीचे मेरे परीक्षण हैं (मैं वास्तव में और अधिक था, लेकिन इसे कम करने में मदद करने के लिए उनसे छुटकारा पा लिया। मैंने इसे पढ़ने में आपकी सहायता के लिए कुछ टिप्पणियां भी शामिल की हैं)। मैंने सी ++ में प्रोजेक्ट किया था, इसलिए मैंने Google के सी ++ परीक्षण ढांचे (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();
}
सब कुछ कहा और किया जाने के बाद ... मैंने ऐसा क्यों किया ?? सबसे पहले सबसे अच्छा। मैंने कोडों के प्रत्येक टुकड़े को लिखने के लिए तैयार होने के रूप में वृद्धि को लिखा है। इससे मुझे दिमाग की शांति मिली कि मैंने जो कोड पहले से लिखा है वह ठीक से काम कर रहा था। यह मेरे सभी कोड लिखने के लिए पागल हो गया था और फिर बस एक फ़ाइल पर आज़माएं और देखें कि क्या हुआ। इतनी सारी परतें थीं, मुझे कैसे पता चलेगा कि एक बग कहां से आएगा जब तक कि मेरे पास अलगाव में प्रत्येक छोटे टुकड़े का परीक्षण न हो?
लेकिन ... सबसे महत्वपूर्ण !!! परीक्षण वास्तव में आपके कोड में प्रारंभिक बग को पकड़ने के बारे में नहीं है ... यह आपके कोड को गलती से तोड़ने से बचाने के बारे में है। हर बार जब मैंने अपनी इलोकपर्सर कक्षा को दोबारा बदल दिया या बदल दिया, तो मुझे विश्वास था कि मैंने इसे खराब तरीके से नहीं बदला क्योंकि मैं अपने परीक्षण (सेकंड के मामले में) चला सकता था और देख सकता हूं कि सभी कोड अभी भी अपेक्षित काम कर रहे हैं। यह यूनिट परीक्षणों का शानदार उपयोग है।
ऐसा लगता है कि वे बहुत अधिक समय लेते हैं ... लेकिन आखिरकार, वे आपको बग्स को ट्रैक करने में समय बचाते हैं क्योंकि आपने कुछ कोड बदल दिया है और नहीं पता कि क्या हुआ। वे यह सत्यापित करने का एक उपयोगी तरीका हैं कि कोड के छोटे टुकड़े ऐसा कर रहे हैं जो उन्हें करना है, और सही ढंग से।
डुप्लिकेट का डुप्लिकेट। "यूनिट टेस्ट क्या है" के लिए बस स्टैक ओवरफ़्लो खोजें और आपके पास महीनों तक चबाने के लिए पर्याप्त होगा। – womp
@womp: मैंने अभी किया और मुझे समान शीर्षक वाले किसी को भी नहीं देखा। –
http://stackoverflow.com/search?q=unit+testing – Nifle