SlideShare una empresa de Scribd logo
1 de 5
Descargar para leer sin conexión
How	to	complement	TDD	with	static	
analysis
Author: Andrey Karpov
Date: 12.12.2012
TDD is one of the most popular software development techniques. I like this technology in general, and we
employ it to some extent. The main thing is not to run to extremes when using it. One shouldn't fully rely on
it alone forgetting other methods of software quality enhancement. In this article, I will show you how the
static code analysis methodology can be used by programmers using TDD to additionally secure themselves
against errors.
TDD is wonderful
Test-driven development (TDD) is a technique of software development based on iteration of very short
development cycles. You write a test first which covers the change you want to introduce, then you write a
code to pass the test, and finally you carry out refactoring of the new code to meet the corresponding
standards. I won't dwell on what TDD is: there exist many articles on this subject which you can easily find
on the Internet.
I think it's especially important not to let yourself be carried away by creating numerous tests when using
TDD. Tests allow you to show a delusive whirl of activity writing a huge number of code lines per day. But at
the same time the product's functionality will grow very slowly. You may spend almost all your effort and
time on writing test codes. Moreover, tests are sometimes labor-intensive to maintain when the
functionality changes.
That's why we don't use TDD in its pure form when developing PVS-Studio. If we write tests for individual
functions, the development time will grow several dozens of times. The reason is this: to call a function
expanding a type in typedef or perform some code analysis, we have to prepare quite a lot of input data.
We also need to build a correct fragment of the parse tree in memory and fill a lot of structures. All this
takes too much time.
We use another technique. Our TDD tests are small C/C++ code fragments marked in a special way. At first
we write various situations where certain warnings are to be generated. Then we start implementing the
code to detect them. In rough outline, these tests look something like this:
int A() {
int x;
return x; //Err
}
This test checks that the program generates a warning about the use of an uninitialized variable. This error
doesn't exist at first, of course. We implement the diagnostic and then add new tests for unique situations.
int B() {
static int x;
return x; //Ok
}
All is good here, as the variable is a static one.
This is, of course, not a canonical way of using TDD. But it's the result which is important, not the form, isn't
it? The idea is the same: we start with a set of tests which are not passed; then implement the diagnostic,
write new texts, carry out refactoring, and so on.
TDD in its pure form cannot be used everywhere. For example, such is our case. If you want to use this
methodology, but it's not convenient to you, try to look at it from a higher abstraction level. We think we've
managed that.
TDD is wonderful but don't go mad about it
If you use a huge number of tests, it may give you a false sense of safety, which makes programmers reduce
the code quality control. TDD allows you to detect many defects at the development stage - but never all of
them. Don't forget the other testing methodologies.
When studying the source codes of many open-source applications, I constantly notice the same two
drawbacks of unit-test usage. TDD does have other ones, but I won't speak on them now. At least, they
don't attract my attention that much.
So, these are the two typical problems when making tests:
1) Tests themselves are not tested.
2) Tests don't check rare critical cases.
Writing tests for tests is really too much. But we should keep in mind that a test is a program code too, and
errors may occur there as well. There are frequent cases when tests only pretend to check something.
What to do? You should use additional tools for code quality control, at least. These may be dynamic or
static code analyzers. They don't guarantee detection of all the errors in tests, of course, but the use of
various tools in a complex produces very good results.
For example, I often come across errors in test codes when running PVS-Studio to check a new project. Here
is an example taken from the Chromium project.
TEST(SharedMemoryTest, MultipleThreads) {
....
int threadcounts[] = { 1, kNumThreads };
for (size_t i = 0;
i < sizeof(threadcounts) / sizeof(threadcounts); i++) {
....
}
Some of the tests must be launched in one thread and then in several threads. Because of a misprint, the
parallel algorithm work is not tested. The error is here: sizeof(threadcounts) / sizeof(threadcounts).
The following principle will to a large extent secure you against mistakes in tests. A freshly written test
mustn't be passed: it helps you make sure that the test really checks something. Only after that you may
start implementing the new functionality.
However, it doesn't prevent errors in tests all the times. The code shown above won't be passed at first too,
since the error is only in the number of parallel threads to be launched.
We have some more examples. A typical mistake when comparing buffers is mixing up pointer sizes and
buffer sizes: quite often the pointer size is calculated instead of the buffer size. These errors may look
something like this:
bool Test()
{
char *buf = new char[10];
FooFoo(buf);
bool ok = memcmp(buf, "1234567890", sizeof(buf)) == 0;
delete [] buf;
return ok;
}
This test works "by half": it compares only the first 4 or 8 bytes. The number of bytes being compared
depends on the pointer size. This test may look good and correct but don't trust it.
Another weak point of TDD is absence of tests for critical situations. You can create these tests, of course.
But it is unreasonably labor-intensive. For instance, it will take you many efforts to make malloc() return
NULL when needed, while its use is very little. The probability of this situation may be lower than 0.0001%.
So you have to make a compromise between the tests' fullness and laboriousness of their implementation.
Let's play with numbers a bit. Assume the malloc() function is used 1000 times in the code. Let the
probability of memory shortage when calling each of them is 0.0001%. Let's calculate the probability of the
memory allocation error when executing the program:
(1 - 0.999999^1000) * 100% = 0.09995%
The memory shortage probability is approximately 0.1%. It's wasteful to write 1000 tests for these cases. On
the other hand, 0.1% is not that little. Some users will definitely have them. How to make sure they will be
correctly handled?
That's a difficult question. Writing unit-tests is too expensive. Dynamic analyzers are not suitable for the
same reasons: they require that you create a situation when the program lacks memory at certain
moments. Manual testing goes without mentioning.
There are two ways. You may use special tools returning the error code when calling certain system
functions. I never dealt with these systems myself, so I can't say how much simple, efficient and safe they
are.
Another way is to use the static code analyzer. This tool doesn't care how often this or that program branch
is executed: it checks almost the whole code. The word "almost" means that C/C++ programs may contain
"#ifdef" and explicitly disabled branches (through "if(0)") about whose contents we'd better not speak.
Here is an example of a bug detected through static analysis in error handlers:
VTK_THREAD_RETURN_TYPE vtkTestCondVarThread( void* arg )
{
....
if ( td ) <<<---
{
....
}
else
{
cout << "No thread data!n";
cout << " Thread " << ( threadId + 1 )
<< " of " << threadCount << " exiting.n";
-- td->NumberOfWorkers; <<<---
cout.flush();
}
...
}
If the error occurs, the message is generated and the variable "td->NumberOfWorkers" gets modified. One
mustn't do it because the 'td' pointer equals zero.
Conclusions
This is my summary of the article:
1. TDD is a wonderful technology. You should spend some time on studying it and start using it in your work.
If the classic TDD doesn't suit you, don't abandon this methodology right away. Perhaps you will be able to
use it if you consider using it a bit differently or at a higher abstraction level.
2. Don't go mad about it. Ideal methodologies don't exist. Tests check far not all the code in practice, and
tests themselves are also error-prone. Use other testing methods: load testing, static code analysis and
dynamic code analysis.

Más contenido relacionado

La actualidad más candente

Introduction To J unit
Introduction To J unitIntroduction To J unit
Introduction To J unit
Olga Extone
 

La actualidad más candente (19)

Manual testing interview question by INFOTECH
Manual testing interview question by INFOTECHManual testing interview question by INFOTECH
Manual testing interview question by INFOTECH
 
Refactoring legacy code driven by tests - ENG
Refactoring legacy code driven by tests - ENGRefactoring legacy code driven by tests - ENG
Refactoring legacy code driven by tests - ENG
 
Junit and cactus
Junit and cactusJunit and cactus
Junit and cactus
 
Test driven development in .Net - 2010 + Eclipse
Test driven development in .Net - 2010 + EclipseTest driven development in .Net - 2010 + Eclipse
Test driven development in .Net - 2010 + Eclipse
 
Introduction To J unit
Introduction To J unitIntroduction To J unit
Introduction To J unit
 
DotNet unit testing training
DotNet unit testing trainingDotNet unit testing training
DotNet unit testing training
 
TDD reloaded - JUGTAA 24 Ottobre 2012
TDD reloaded - JUGTAA 24 Ottobre 2012TDD reloaded - JUGTAA 24 Ottobre 2012
TDD reloaded - JUGTAA 24 Ottobre 2012
 
Google test training
Google test trainingGoogle test training
Google test training
 
An Introduction to Test Driven Development
An Introduction to Test Driven Development An Introduction to Test Driven Development
An Introduction to Test Driven Development
 
Unit Testing 101
Unit Testing 101Unit Testing 101
Unit Testing 101
 
Testing techniques
Testing techniquesTesting techniques
Testing techniques
 
Automation testing interview pdf org
Automation testing interview pdf orgAutomation testing interview pdf org
Automation testing interview pdf org
 
Test-Driven Development
Test-Driven DevelopmentTest-Driven Development
Test-Driven Development
 
Automation frameworks
Automation frameworksAutomation frameworks
Automation frameworks
 
JUnit Presentation
JUnit PresentationJUnit Presentation
JUnit Presentation
 
TDD (Test Driven Design)
TDD (Test Driven Design)TDD (Test Driven Design)
TDD (Test Driven Design)
 
Unit testing - the hard parts
Unit testing - the hard partsUnit testing - the hard parts
Unit testing - the hard parts
 
Qa mockup interview for automation testing
Qa mockup interview for automation testingQa mockup interview for automation testing
Qa mockup interview for automation testing
 
Formal method
Formal methodFormal method
Formal method
 

Destacado

Destacado (20)

Visual Studio commands
Visual Studio commandsVisual Studio commands
Visual Studio commands
 
I want to sell a PVS-Studio license to the Intel company
I want to sell a PVS-Studio license to the Intel companyI want to sell a PVS-Studio license to the Intel company
I want to sell a PVS-Studio license to the Intel company
 
64-bit
64-bit64-bit
64-bit
 
R&D on PVS-Studio
R&D on PVS-StudioR&D on PVS-Studio
R&D on PVS-Studio
 
Monitoring a program that monitors computer networks
Monitoring a program that monitors computer networksMonitoring a program that monitors computer networks
Monitoring a program that monitors computer networks
 
A few words about OpenSSL
A few words about OpenSSLA few words about OpenSSL
A few words about OpenSSL
 
Errors detected in the Visual C++ 2012 libraries
Errors detected in the Visual C++ 2012 librariesErrors detected in the Visual C++ 2012 libraries
Errors detected in the Visual C++ 2012 libraries
 
Analysis of the Trans-Proteomic Pipeline (TPP) project
Analysis of the Trans-Proteomic Pipeline (TPP) projectAnalysis of the Trans-Proteomic Pipeline (TPP) project
Analysis of the Trans-Proteomic Pipeline (TPP) project
 
How we test the code analyzer
How we test the code analyzerHow we test the code analyzer
How we test the code analyzer
 
Checking OpenCV with PVS-Studio
Checking OpenCV with PVS-StudioChecking OpenCV with PVS-Studio
Checking OpenCV with PVS-Studio
 
How to make fewer errors at the stage of code writing. Part N4.
How to make fewer errors at the stage of code writing. Part N4.How to make fewer errors at the stage of code writing. Part N4.
How to make fewer errors at the stage of code writing. Part N4.
 
Why Windows 8 drivers are buggy
Why Windows 8 drivers are buggyWhy Windows 8 drivers are buggy
Why Windows 8 drivers are buggy
 
Cppcheck and PVS-Studio compared
Cppcheck and PVS-Studio comparedCppcheck and PVS-Studio compared
Cppcheck and PVS-Studio compared
 
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
 
The compiler is to blame for everything
The compiler is to blame for everythingThe compiler is to blame for everything
The compiler is to blame for everything
 
Visual Studio Automation Object Model. EnvDTE interfaces
Visual Studio Automation Object Model. EnvDTE interfacesVisual Studio Automation Object Model. EnvDTE interfaces
Visual Studio Automation Object Model. EnvDTE interfaces
 
Integrating into Visual Studio settings
Integrating into Visual Studio settingsIntegrating into Visual Studio settings
Integrating into Visual Studio settings
 
100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects
 
PVS-Studio vs Chromium
PVS-Studio vs ChromiumPVS-Studio vs Chromium
PVS-Studio vs Chromium
 
How to make fewer errors at the stage of code writing. Part N3.
How to make fewer errors at the stage of code writing. Part N3.How to make fewer errors at the stage of code writing. Part N3.
How to make fewer errors at the stage of code writing. Part N3.
 

Similar a How to complement TDD with static analysis

Test Driven iOS Development (TDD)
Test Driven iOS Development (TDD)Test Driven iOS Development (TDD)
Test Driven iOS Development (TDD)
Babul Mirdha
 
TDD - survival guide
TDD - survival guide TDD - survival guide
TDD - survival guide
vitalipe
 

Similar a How to complement TDD with static analysis (20)

Python and test
Python and testPython and test
Python and test
 
TDD Best Practices
TDD Best PracticesTDD Best Practices
TDD Best Practices
 
assertYourself - Breaking the Theories and Assumptions of Unit Testing in Flex
assertYourself - Breaking the Theories and Assumptions of Unit Testing in FlexassertYourself - Breaking the Theories and Assumptions of Unit Testing in Flex
assertYourself - Breaking the Theories and Assumptions of Unit Testing in Flex
 
The limits of unit testing by Craig Stuntz
The limits of unit testing by Craig StuntzThe limits of unit testing by Craig Stuntz
The limits of unit testing by Craig Stuntz
 
The Limits of Unit Testing by Craig Stuntz
The Limits of Unit Testing by Craig StuntzThe Limits of Unit Testing by Craig Stuntz
The Limits of Unit Testing by Craig Stuntz
 
Test-Driven Development In Action
Test-Driven Development In ActionTest-Driven Development In Action
Test-Driven Development In Action
 
Test-Driven Developments are Inefficient; Behavior-Driven Developments are a ...
Test-Driven Developments are Inefficient; Behavior-Driven Developments are a ...Test-Driven Developments are Inefficient; Behavior-Driven Developments are a ...
Test-Driven Developments are Inefficient; Behavior-Driven Developments are a ...
 
Test driven development
Test driven developmentTest driven development
Test driven development
 
TDD Workshop UTN 2012
TDD Workshop UTN 2012TDD Workshop UTN 2012
TDD Workshop UTN 2012
 
Unit testing
Unit testingUnit testing
Unit testing
 
Why test with flex unit
Why test with flex unitWhy test with flex unit
Why test with flex unit
 
The D language comes to help
The D language comes to helpThe D language comes to help
The D language comes to help
 
Tdd is not about testing (OOP)
Tdd is not about testing (OOP)Tdd is not about testing (OOP)
Tdd is not about testing (OOP)
 
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
 
unit_tests_tutorial
unit_tests_tutorialunit_tests_tutorial
unit_tests_tutorial
 
Test Driven iOS Development (TDD)
Test Driven iOS Development (TDD)Test Driven iOS Development (TDD)
Test Driven iOS Development (TDD)
 
Tdd is not about testing (C++ version)
Tdd is not about testing (C++ version)Tdd is not about testing (C++ version)
Tdd is not about testing (C++ version)
 
Tdd
TddTdd
Tdd
 
TDD - survival guide
TDD - survival guide TDD - survival guide
TDD - survival guide
 
Accord.Net: Looking for a Bug that Could Help Machines Conquer Humankind
Accord.Net: Looking for a Bug that Could Help Machines Conquer HumankindAccord.Net: Looking for a Bug that Could Help Machines Conquer Humankind
Accord.Net: Looking for a Bug that Could Help Machines Conquer Humankind
 

Último

CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
Enterprise Knowledge
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Último (20)

CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 

How to complement TDD with static analysis

  • 1. How to complement TDD with static analysis Author: Andrey Karpov Date: 12.12.2012 TDD is one of the most popular software development techniques. I like this technology in general, and we employ it to some extent. The main thing is not to run to extremes when using it. One shouldn't fully rely on it alone forgetting other methods of software quality enhancement. In this article, I will show you how the static code analysis methodology can be used by programmers using TDD to additionally secure themselves against errors. TDD is wonderful Test-driven development (TDD) is a technique of software development based on iteration of very short development cycles. You write a test first which covers the change you want to introduce, then you write a code to pass the test, and finally you carry out refactoring of the new code to meet the corresponding standards. I won't dwell on what TDD is: there exist many articles on this subject which you can easily find on the Internet. I think it's especially important not to let yourself be carried away by creating numerous tests when using TDD. Tests allow you to show a delusive whirl of activity writing a huge number of code lines per day. But at the same time the product's functionality will grow very slowly. You may spend almost all your effort and time on writing test codes. Moreover, tests are sometimes labor-intensive to maintain when the functionality changes. That's why we don't use TDD in its pure form when developing PVS-Studio. If we write tests for individual functions, the development time will grow several dozens of times. The reason is this: to call a function expanding a type in typedef or perform some code analysis, we have to prepare quite a lot of input data. We also need to build a correct fragment of the parse tree in memory and fill a lot of structures. All this takes too much time. We use another technique. Our TDD tests are small C/C++ code fragments marked in a special way. At first we write various situations where certain warnings are to be generated. Then we start implementing the code to detect them. In rough outline, these tests look something like this: int A() { int x; return x; //Err }
  • 2. This test checks that the program generates a warning about the use of an uninitialized variable. This error doesn't exist at first, of course. We implement the diagnostic and then add new tests for unique situations. int B() { static int x; return x; //Ok } All is good here, as the variable is a static one. This is, of course, not a canonical way of using TDD. But it's the result which is important, not the form, isn't it? The idea is the same: we start with a set of tests which are not passed; then implement the diagnostic, write new texts, carry out refactoring, and so on. TDD in its pure form cannot be used everywhere. For example, such is our case. If you want to use this methodology, but it's not convenient to you, try to look at it from a higher abstraction level. We think we've managed that. TDD is wonderful but don't go mad about it If you use a huge number of tests, it may give you a false sense of safety, which makes programmers reduce the code quality control. TDD allows you to detect many defects at the development stage - but never all of them. Don't forget the other testing methodologies. When studying the source codes of many open-source applications, I constantly notice the same two drawbacks of unit-test usage. TDD does have other ones, but I won't speak on them now. At least, they don't attract my attention that much. So, these are the two typical problems when making tests: 1) Tests themselves are not tested. 2) Tests don't check rare critical cases. Writing tests for tests is really too much. But we should keep in mind that a test is a program code too, and errors may occur there as well. There are frequent cases when tests only pretend to check something. What to do? You should use additional tools for code quality control, at least. These may be dynamic or static code analyzers. They don't guarantee detection of all the errors in tests, of course, but the use of various tools in a complex produces very good results. For example, I often come across errors in test codes when running PVS-Studio to check a new project. Here is an example taken from the Chromium project. TEST(SharedMemoryTest, MultipleThreads) {
  • 3. .... int threadcounts[] = { 1, kNumThreads }; for (size_t i = 0; i < sizeof(threadcounts) / sizeof(threadcounts); i++) { .... } Some of the tests must be launched in one thread and then in several threads. Because of a misprint, the parallel algorithm work is not tested. The error is here: sizeof(threadcounts) / sizeof(threadcounts). The following principle will to a large extent secure you against mistakes in tests. A freshly written test mustn't be passed: it helps you make sure that the test really checks something. Only after that you may start implementing the new functionality. However, it doesn't prevent errors in tests all the times. The code shown above won't be passed at first too, since the error is only in the number of parallel threads to be launched. We have some more examples. A typical mistake when comparing buffers is mixing up pointer sizes and buffer sizes: quite often the pointer size is calculated instead of the buffer size. These errors may look something like this: bool Test() { char *buf = new char[10]; FooFoo(buf); bool ok = memcmp(buf, "1234567890", sizeof(buf)) == 0; delete [] buf; return ok; } This test works "by half": it compares only the first 4 or 8 bytes. The number of bytes being compared depends on the pointer size. This test may look good and correct but don't trust it. Another weak point of TDD is absence of tests for critical situations. You can create these tests, of course. But it is unreasonably labor-intensive. For instance, it will take you many efforts to make malloc() return NULL when needed, while its use is very little. The probability of this situation may be lower than 0.0001%. So you have to make a compromise between the tests' fullness and laboriousness of their implementation.
  • 4. Let's play with numbers a bit. Assume the malloc() function is used 1000 times in the code. Let the probability of memory shortage when calling each of them is 0.0001%. Let's calculate the probability of the memory allocation error when executing the program: (1 - 0.999999^1000) * 100% = 0.09995% The memory shortage probability is approximately 0.1%. It's wasteful to write 1000 tests for these cases. On the other hand, 0.1% is not that little. Some users will definitely have them. How to make sure they will be correctly handled? That's a difficult question. Writing unit-tests is too expensive. Dynamic analyzers are not suitable for the same reasons: they require that you create a situation when the program lacks memory at certain moments. Manual testing goes without mentioning. There are two ways. You may use special tools returning the error code when calling certain system functions. I never dealt with these systems myself, so I can't say how much simple, efficient and safe they are. Another way is to use the static code analyzer. This tool doesn't care how often this or that program branch is executed: it checks almost the whole code. The word "almost" means that C/C++ programs may contain "#ifdef" and explicitly disabled branches (through "if(0)") about whose contents we'd better not speak. Here is an example of a bug detected through static analysis in error handlers: VTK_THREAD_RETURN_TYPE vtkTestCondVarThread( void* arg ) { .... if ( td ) <<<--- { .... } else { cout << "No thread data!n"; cout << " Thread " << ( threadId + 1 ) << " of " << threadCount << " exiting.n"; -- td->NumberOfWorkers; <<<---
  • 5. cout.flush(); } ... } If the error occurs, the message is generated and the variable "td->NumberOfWorkers" gets modified. One mustn't do it because the 'td' pointer equals zero. Conclusions This is my summary of the article: 1. TDD is a wonderful technology. You should spend some time on studying it and start using it in your work. If the classic TDD doesn't suit you, don't abandon this methodology right away. Perhaps you will be able to use it if you consider using it a bit differently or at a higher abstraction level. 2. Don't go mad about it. Ideal methodologies don't exist. Tests check far not all the code in practice, and tests themselves are also error-prone. Use other testing methods: load testing, static code analysis and dynamic code analysis.