Reputation: 399
first thing to emphases is that my question is NOT about error handling in constructor. So I'm doing this assignment to writing a Date class, and first thing is about the constructor, of course it has to be able to handle invalid date input, I already have my constructor implemented as shown below with the error handling part implemented using try-catch:
My Date Constructor:
Date(unsigned y, unsigned m, unsigned d)
{
try {
check_valid(y, m, d);
my_year = y; my_month = m; my_day = d;
}
catch (const std::exception& msg) {
std::cerr << msg.what() << std::endl;
}
}
check_valid function:
void Date::check_valid(unsigned y, unsigned m, unsigned d)
{
MYASSERT(y >= 1900 && y <2200, "The year is invalid");
MYASSERT(m >= 1 && m <= 12, "The year is invalid");
MYASSERT(d >= 1 && d <= dmax, "The year is invalid"); //dmax is just no. of days in the month
}
#define MYASSERT(cond, msg) \
{ \
if (!(cond)) \
{ \
throw std::invalid_argument(msg); \
} \
}
The Question: I'm asked to write a back testing program: randomly generate a large number of INVALID date (with the seed recorded) to test if the constructor is able to perform error handling successfully. Since input is a invalid date, every test should throw an expectation. So, if some test fails (meaning doesn't throw an exception given a invalid date input) Print out the random seed used for the random number generator, so that a programmer can re-use the same seed and reproduce the error.
I'm stuck with how to do this, how do i check if an expectation msg is throw? what should go into the if statement?
while (counter < 1000) {
seed = rand();
srand(seed);
unsigned y = rand() % 500 + 1800; //rand year between (1800, 2299)
unsigned m = rand() % 20; //rand month between (0, 19)
unsigned d = rand() % 40; //rand day between (0, 39)
if (! isValidDate(y, m, d)) //some function to filter out the valid date
{
counter++;
Date somedate(y, m, d); //use the constructor
{
// the constructor is used above, but i have no idea if an expectation is thrown or not
// if an expectation is thrown, then print seed, how do i write this code?
}
}
}
Upvotes: 1
Views: 86
Reputation: 4869
I played around a little - again not complete since no calender is checked: See also code on cpp.sh
#include <iostream>
#include <string>
#include <stdexcept>
class Date
{
private:
unsigned my_year;
unsigned my_month;
unsigned my_day;
void assertValid( void );
void assertYear( void );
void assertMonth( void );
void assertDay( void );
void fail( std::string message );
public:
Date( unsigned y, unsigned m, unsigned d);
bool isLeapYear( void );
};
Date::Date(unsigned y, unsigned m, unsigned d)
{
my_year = y;
my_month = m;
my_day = d;
assertValid();
}
//simple checks for Gregorian calendar
void Date::assertValid( void )
{
assertYear();
assertMonth();
assertDay();
}
void Date::assertYear( void )
{
if( my_year < 1900 || my_year > 2200 )
{
fail( "invalid year, must be >1900 and < 2200" );
}
}
void Date::assertMonth( void )
{
if( my_month < 1 || my_month > 12 )
{
fail("invalid month, must be 1 - =12");
}
}
void Date::assertDay( void )
{
if( my_day == 0 )
{
fail("day must not be 0");
}
switch( my_month )
{
case 4:
case 6:
case 9:
case 11:
if( my_day > 30 )
{
fail("invalid day of month, must be <31");
}
break;
case 2:
if( isLeapYear() && my_day > 29 )
{
fail("invalid day of month, must be < 30");
}
else if( my_day > 28 )
{
fail("invalid day of month, must be < 30");
}
break;
default:
if( my_day > 31 )
{
fail("invalid day of month, must be < 32");
}
}
}
bool Date::isLeapYear( void )
{
return my_year % 4 == 0
&& ( my_year % 100 != 0
|| my_year % 400 == 0 )
;
}
void Date::fail( std::string message )
{
throw std::invalid_argument(message);
}
bool testDate( unsigned year, unsigned month, unsigned day, bool isValidDate )
{
try
{
Date( year, month, day );
}
catch( std::invalid_argument& x)
{
if( ! isValidDate )
{
return true;
}
}
return isValidDate;
}
int main()
{
int failures = 0;
int successes = 0;
testDate( 1,1,1, false) ? ++successes : ++failures;
testDate( 2000,2,29, true) ? ++successes : ++failures;
testDate( 1900, 2, 29, false ) ? ++successes : ++failures;
testDate( 1980, 1,1, true ) ? ++successes : ++failures;
std::cout << "Number of tetsts: " << successes + failures << std::endl;
std::cout << "Number of failures: " << failures <<std::endl;
}
Upvotes: 0
Reputation: 3983
I recently came across a blog about testing at Google, where they linked an example of how they write and test code. One of their cases there looks very much like something you could use here (testing that something should fail, and as other comments mentioned; throw an exception):
public void testStart_whileRunning() {
stopwatch.start();
try {
stopwatch.start();
fail();
} catch (IllegalStateException expected) {
}
assertTrue(stopwatch.isRunning());
}
The example is in Java, but the principle is identical in C++: Have a fail()
method that fails the test unconditionally if it is run, but which is skipped if your code "correctly fails".
Upvotes: 1