190 likes | 213 Views
Learn the principles, benefits, and components of unit testing using the CppUnit framework in C++. Discover the importance of fault identification, correction, and automation for efficient software testing.
E N D
Advanced program design with c++ Unit Testing Frameworks CppUnit Joey Paquet, 2016
Software testing • Software testing is meant to avoid software failure. • A failure is caused by a fault in the code base. • A symptom is an observable behavior of the system that enables us to observe a failure and possibly find its corresponding fault. • The process of discovering what caused a failure is called fault identification. • The process of ensuring that the failure does not happen again is called fault correction, or fault removal. • Fault identification and fault correction is popularly called debugging. • Software testing, in practice, is about identifying a certain possible system failure and design a test case that proves that this particular failure is not experienced by the software. • “testing can reveal only the presence of faults, never their absence.” [Edsger Dijkstra] Joey Paquet, 2016
Software testing • There are many driving sources for software testing: • Requirements-driven testing, Structure-driven testing, Statistics-driven testing, Risk-driven testing. • There are many levels and kinds of software testing: • Unit Testing, Integration Testing, Function Testing, Acceptance Testing, Installation Testing. • Unit testing can easily be integrated in the programming effort by using a Unit Testing Framework. • However, unit testing cannot be applied for higher-level testing purposes such as function testing or acceptance testing, which are system-level testing activities. Joey Paquet, 2016
Unit testing • Defintion: A unit test is a piece of code written by a developer that exercises a very small, specific area of functionality applied to one of the units of the code being tested. Usually a unit test exercises some particular method in a particular context. • Example: add a large value to a sorted list, then confirm that this value appears at the end of the list. • The goal of unit testing is to isolate important parts (i.e. units) of the program and show that the individual parts are free of certain faults. Joey Paquet, 2016
Unit testing: benefits • Facilitates change: • Unit testing allows the programmer to change or refactor code later, and make sure the module still works correctly after the change (i.e. regression testing). • Simplifies integration: • Unit testing helps to eliminate uncertainty in the units and can be used in a bottom-up integration testing style approach. • Documentation: • Unit testing provides a sort of living documentation of the specifications of the units of the system. Developers looking to learn what functionality is provided by a unit and how to use it can look at the unit tests to gain understanding of the unit’s API specifications. Joey Paquet, 2016
Unit testing: benefits • Distributes feedback: • Identifies defects early and regularly in the development cycle and avoids to cluster the finding of bugs in the later stages. • Confidence building: • Successful (and meaningful) tests builds up confidence in the code to everyone involved, which then may lead to write more tests and thus even more completeness in testing. • Forces analysis: • Writing unit tests forces the programmers to read and analyze their code, thus removing defects through constant code verification. Joey Paquet, 2016
Unit testing framework • For a large system, there can be thousands of unit tests, which can be tedious to maintain and execute. • Automated testing supports maintainability and extensibility along with efficiency. • A xUnit Testing Framework lets a programmer associate Classes and Methods to corresponding Test Classes and Test Methods. • Automation is achieved by automatically setting up a testing context, calling each test case, verifying their corresponding expected result, and reporting the status of all tests. • Can be combined with the use of a Software Versioning Repository: prior to any commit being made, unit testing is re-applied to make sure that the committed code is still working properly. Joey Paquet, 2016
Unit testing framework: components • Tested Class – the class that is being tested. • Tested Method – the method that is tested. • Test Case – the testing of a class’s method against some specified conditions. • Test Case Class – a class performing some test cases. • Test Case Method – a Test Case Class’s method implementing a test case. • Test Suite – a collection of test cases that can be tested in a single batch. Joey Paquet, 2016
C++ unit testing framework: CppUnit • In C++, the standard unit testing framework is known as CppUnit. • Test Cases and Test Results are C++ objects. • The concept of a unit testing framework was created by Erich Gamma and Kent Beck, two authors best known for Design Patterns and eXtreme Programming, respectively. • Using CppUnit you can easily and incrementally build a test suite that will help you measure your progress, spot unintended side effects, and focus your development efforts. Joey Paquet, 2016
CppUnit: test case procedure • Every test case aims at testing that a certain method has the effect of returning or changing a value according to a certain correct behavior in a certain context. • Thus, every test case has three phases: • Set up the context in which the function will be called. • Call the function. • Observe if the function call resulted in the correct behavior using one or more CppUnit assertion method. Joey Paquet, 2016
CppUnit: setting up the testing context • All test cases represent an execution in a certain context. • The context is represented by: • The values passed to the function as parameters. • Other variables that the function may be using internally, which are not passed as parameters. • All of these must be set up before the call to the tested function. • The values passed to or used by the function can be declared as: • Local variables in the test case method. • Data members of the test class. • Declaring the contextual data as data members of the test class allows several test case methods to share the same contextual data elements. • If more than one test case share the same context, group them in a test class and use setUp()/tearDown() to set the context to the right values before each test case is run. Joey Paquet, 2016
CppUnit: assertions • The CppUnit framework provides a complete set of assertion methods that can be used in different situations: CPPUNIT_ASSERT(condition) Asserts that a condition is true. CPPUNIT_ASSERT_EQUAL(expected, actual) Asserts that two values are equals. CPPUNIT_ASSERT_THROW(expression, ExceptionType) Asserts that the given expression throws an exception of the specified type. CPPUNIT_ASSERT_NO_THROW(expression) Asserts that the given expression does not throw any exceptions. • See CppUnit documentation for more. Joey Paquet, 2016
CppUnit: executing the tests • A CppUnit test class is executable by a test runner. • Most Java IDEs have a JUnit test runner embedded as a plugin: • Basic CppUnit has more primitive test runners that output to the console: • Eclipse Juno also has a CppUnit plugin: Joey Paquet, 2016
CppUnit: examples of Test Class and Test Cases //! Header file: //! A CppUnit Test Class is a TestFixture classTestCalculations : publicCppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestCalculations);//declare the test suite CPPUNIT_TEST(testDivision);//test the Division method CPPUNIT_TEST(testMultiplication);//test the multiplication method CPPUNIT_TEST(testAddition);//test the addition method CPPUNIT_TEST(testSubtraction);//test the addition method CPPUNIT_TEST_SUITE_END();//end of test suite declaration public: voidsetUp(void); voidtearDown(void); protected: voidtestDivision(void); voidtestAddition(void); voidtestMultiplication(void); voidtestSubtraction(void); private: Calculations *CalcObject; }; //! Register for running the test CPPUNIT_TEST_SUITE_REGISTRATION(TestCalculations); //! Cpp file: //! setUp() method that is executed before all the test cases //! in case they all share a common context of use voidTestCalculations::setUp(void) { CalcObject = newCalculations(); } //! tearDown() method that is executed after all the test cases //! typically does memory deallocation voidTestCalculations::tearDown(void) { deleteCalcObject; } //! test method to test the Addition() method //! Test Case: 3+3=6 voidTestCalculations::testAddition(void) { CPPUNIT_ASSERT(6 == CalcObject->Addition(3,3)); } //! test method to test the Multiplication() method //! Test Case: 2*3=6 voidTestCalculations::testMultiplication(void) { CPPUNIT_ASSERT(6 == CalcObject->Multiplication(2,3)); } //! Calculations (Tested Class) //! Nothing needs to be added to a Tested Class classCalculations { public: float Division(float a, float b); float Multiplication(float a, float b); float Addition (float a, float b); float Subtraction (float a , float b); }; Joey Paquet, 2016
Tested Class vs. Test Class • Nothing needs to be added to the tested class. • Every test case tests a call to a method of the tested class in a predefined context and asserts whether its call resulted in a predicted behavior. • Each test case is implemented as a method of a CppUnit test class. • Test cases that share the same context elements can be gathered in a single test class. • If they all share the same context, the test class can define a setup() and tearDown() function, as well as use variables declared as members of their test class to store their common context. • Typically, the context is: • a variable of the tested class type. • Values used in the creation of an object of the test class type. • parameters passed to the tested function. Joey Paquet, 2016
CppUnit Test Class • CppUnit test class: • Must be a subclass of TestFixture. • The test cases are methods of a test class (typically starting with “test”). • The test methods must be included in a test registry, which creates a test suite that is later going to be used by the test runner to run all the tests from all the test classes. • The test class declaration can be put in a header file and the implementation code test cases can be put in a cpp file. //! A CppUnit Test Class is a TestFixture classTestCalculations : publicCppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestCalculations);//declare the test suite CPPUNIT_TEST(testDivision);//test the Division method CPPUNIT_TEST(testMultiplication);//test the multiplication method CPPUNIT_TEST(testAddition);//test the addition method CPPUNIT_TEST(testSubtraction);//test the addition method CPPUNIT_TEST_SUITE_END();//end of test suite declaration public: voidsetUp(void); voidtearDown(void); protected: voidtestDivision(void); voidtestAddition(void); voidtestMultiplication(void); voidtestSubtraction(void); private: Calculations *CalcObject; }; //! Register for running the test CPPUNIT_TEST_SUITE_REGISTRATION(TestCalculations); //! Cpp file: //! setUp() method that is executed before all the test cases //! in case they all share a common context of use //! test method to test the Addition() method //! Test Case: 3+3=6 voidTestCalculations::testAddition(void) { CPPUNIT_ASSERT(6 == CalcObject->Addition(3,3)); } //! test method to test the Multiplication() method //! Test Case: 2*3=6 voidTestCalculations::testMultiplication(void) { CPPUNIT_ASSERT(6 == CalcObject->Multiplication(2,3)); } Joey Paquet, 2016
CppUnit Test Suite and Test Runner • In CppUnit, a test suite is a set of test cases that have been put in the test case registry as they were declared. • A Test Suite is in fact a Composite, i.e. it is a test that is composed of tests. • Allows to group together test cases defined across different test classes. • A CppUnit test case runner takes a test and runs it. If the test is a composite, it will run all the tests that compose it. • An outputter also needs to be set for the runner. • Some IDE plugins will define their own runner/outputter. //! main() function. Entry point of the program //! It does the following: //! 1. Create a test suite object from the registry as populated by the code in the Test Classes //! 2. Create a test runner that will execute all the tests in the registry //! 3. (optionally) sets an outputter that will output the results //! 4. Run the test cases. int main(intargc, char* argv[]) { // Get the top level suite from the registry CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); // Adds the test to the list of test to run CppUnit::TextUi::TestRunner runner; runner.addTest( suite ); // Change the default outputter to a compiler error format outputter runner.setOutputter( newCppUnit::CompilerOutputter( &runner.result(), std::cerr ) ); // Run the tests. boolwasSucessful = runner.run(); getchar(); // Return error code 1 if the one of test failed. returnwasSucessful ? 0 : 1; } Joey Paquet, 2016
CppUnit: Installation and Usage • To install cppUnit : • Unpack the CppUnit archive to a directory of your choice, in this example I assume it is D:\. • Go to D:/cppunit-1.12.1/src and open the CppUnitLibraries.dsw in Visual Studio. • Select the cppunit project in the Solution Explorer and go to 'Project > Properties > Configuration Properties > Librarian > General. Put "Debug\cppunit.lib"in the ‘Output File’ textbox. • Right-click on the cppunit project in the Solution Explorer pane and choose Build. • After successful compilation, D:\cppunit-1.12.1\lib\cppunit.lib is produced which you then need to setup the Visual Studio Linker with (see below). • To setup a project for Compilation/Linking: • Activate 'Project > Properties > Configuration Properties > C/C++ > Code Generation > Runtime Library > Multi - threaded Debug DLL' • Go to 'Project > Properties > Configuration Properties > C/C++ > General'. Put "D:\cppunit-1.12.1\include" in the 'Additional Include Directories' text box. • Go to 'Project > Properties > Configuration Properties > Linker > Input'. Put "D:\cppunit-1.12.1\lib\cppunit.lib" in the 'Additional Dependences' text box. • Go to 'Project > Properties > Configuration Properties > Build Events > Post-Build Event'. Put '"$(TargetPath)"' in the 'Command Line' textbox. Put 'Unit Tests...' in the 'Description' textbox. Joey Paquet, 2016
References • SourceForge. CppUnit Documentation. • SourceForge. CppUnit source code. • Stan Jarzabek, National University of Singapore. Crash Course in using CppUnit. • QA Friend. Example Code using Cpp Unit and Visual Studio. Joey Paquet, 2016