WHAT
WHY
HOW
HOW DO YOu KNOW
THAt YOUR CODE
WORKS?
HOW DO YOu KNOW
THAt YOUR CODE
WORKS?
float sqrRoot(float a);
int main()
{
float input = 4;
float result = sqrRoot( input );
printf("square root of %f is %f\n", input, result);
return 0;
}
// square root of 4 is 2
We usually start with visual inspection...
What is unit testing?
A methodology to test that a UNIT of your code is doing what you want it to do.
A "unit testing framework" is a library that help us automating this process and reducing boilerplate in the code.
float sqrRoot(float a);
int main()
{
float input = 4.0;
float expected = 2.0;
float result = sqrRoot( input );
//warning, this might fail due to floating point comparison
if( result != expected )
{
printf("ERROR: square root of %f is %f instead of %f\n",
input, result, expected );
return ERROR_CODE;
}
bool exception_thrown = false;
try {
sqrRoot( -1 );
}
catch(...) {
exception_thrown = true;
}
if( !exception_thrown ){
printf("ERROR: exception hasn't been thrown by sqrRoot(-1) \n");
return ERROR_CODE;
}
return SUCCESS;
}
When we want to test more...
The "visual inspection" of larger applications
is even more time consuming.
Code
Compile
Observe result
WHy Do WE NEED UNIT TESTING THEN?
OVERALL cost
This is even more true when you consider:
- the cost of debugging
- the fact that you can break functionalities
Spend time preventing bug or fixing bugs?
CODE QUALITY
- If you take it seriously, you are probably testing more edge cases than usual.
- Good tests "oblige" you to have modular code.
- A complex test might be the symptom of a poor API
But of course there are also "bad" tests that are too superficial.
DETECT REGRESSIONS
"it was working yesterday..."
ACCOUNTABILITY
If your code is not tested and not documented,
I might not trust it.
YOu SHOULD NOT Do TESTING IF
- Developing a "proof of concept" to prove an hypothesis
- You are not sure that you (or someone else) will reuse that code
- Cost of bug fixing is low...
You and me know it is not
HOW TO TEST
PRELIMINARY RECOMMENDATIONS
- Test a unit having a single responsibility
- Test edge cases
- The test must be as readable as a tutorial
- If you find a new bug, write the corresponding test.
DON'T obsess
Sometimes 70% is better than 99.9%
GOOGLe testing framework
#include "gtest/gtest.h"
TEST(SquareRootTest, PositiveNos)
{
EXPECT_FLOAT_EQ (2.0, sqrRoot (4.0) );
EXPECT_FLOAT_EQ (18.0, sqrRoot (324.0) );
EXPECT_FLOAT_EQ (25.4, sqrRoot (645.16) );
EXPECT_FLOAT_EQ (50.3321, sqrRoot (2533.310224) );
}
TEST (SquareRootTest, ZeroAndNegativeNos)
{
EXPECT_EQ (0.0, sqrRoot (0.0) );
EXPECT_ANY_THROW ( sqrRoot (-1.0) );
EXPECT_ANY_THROW ( sqrRoot (-22.0) );
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
int main()
{
float input = 4.0;
float expected = 2.0;
float result = sqrRoot( input );
if( fabs(result - expected) < std::numeric_limit<float>::min() )
{
printf("ERROR: square root of %f is %f instead of %f\n",
input, result, expected );
return ERROR_CODE;
}
bool exception_thrown = false;
try {
sqrRoot( -1 );
}
catch(...) {
exception_thrown = true;
}
if( !exception_thrown ){
printf("ERROR: exception hasn't been thrown by sqrRoot(-1) \n");
return ERROR_CODE;
}
return SUCCESS;
}
TEST(SquareRootTest, MinimalTest)
{
EXPECT_FLOAT_EQ (2.0, sqrRoot (4.0));
EXPECT_ANY_THROW ( sqrRoot (-1.0))
}
// To use a test fixture, derive a class from testing::Test.
class DequeTest : public testing::Test {
protected: // You should make the members protected
// virtual void SetUp() will be called before each test is run.
virtual void SetUp()
{
q1_.Enqueue(1);
q1_.Enqueue(3);
q1_.Enqueue(5);
}
// virtual void TearDown() will be called after each test is run.
virtual void TearDown() {}
void copy(List* dq1, List* dq2){
l2->clear();
for(int i=0; l1->size(); i++){
l2->push_back( l1->at(i) );
}
}
// Declares the variables your tests want to use.
Deque<int> q0_;
Deque<int> q1_;
};
// When you have a test fixture, you define a test using TEST_F
TEST_F(DequeTest, DefaultConstructor) {
EXPECT_EQ(0, q0_.Size());
EXPECT_EQ(3, q1_.Size());
}
TEST_F(DequeTest, TestCopy) {
copy( &q1_, &q0_);
ASSERT_EQ( q0_.size() == q1_.size() );
for (int i=0; i< q0_.size(); i++){
EXPECT_EQ( q0_.at(i), q1_.at(i) );
}
}
Fixtures
LIMITATIONS
No matter how many tests you write, you can not "prove" that your code is bug free.
Some bugs appear only in "production".
Summarizing...
References
All your tests are terrible
Pragmatic unit testing in C++
Unit testing
By Davide Faconti
Unit testing
- 633