asp.net mvc display pdf : Change paper size in pdf document SDK control service wpf azure .net dnn Manning%20-%20Code%20Generation%20in%20Action17-part1336

142
CHAPTER 7
G
ENERATING
UNIT
TESTS
7.2.1
Unit tests won’t be maintained
Maintaining production code is difficult enough without having to maintain the sup-
porting unit tests. That may be true, but there are ways to ensure that your company’s
investment in unit tests is maintained:
• Keep the tests simple to create and maintain. One strategy is to keep your tests
close to the code being tested. Another is to use code generation to make build-
ing the tests simple.
• Make the test framework visible and easy to run. Ideally it should also leverage
your existing development tools, or the test framework tools should be installed
as part of the build environment install. No barriers should exist between the
intention to test the code and the actual running of the tests and receiving an
understandable report.
• Support multiple levels of reporting. You should be able to test the overall system
and get back a high-level report telling you what works and where you might run
into problems. The user should be able to drill down on issues or rerun tests in
specific areas to get more information about where issues are located.
• Make unit testing part of the culture. Ensure that time is allocated for unit test
development. Run the unit tests automatically every night with email notification
of failures so that everyone knows when and where unit tests are failing.
7.2.2
It’s too hard to make unit tests
This is sometimes rephrased as “Unit tests won’t work on my code.” If you can write the
code, tools are out there to test the code. It’s true that some types of code are easier to
test than others. You need to analyze how to spend your QA money and balance the
importance of the code being tested against the difficulty and expense of testing it.
For all of the code domains (e.g., database access, interface, RPC) presented in this
book, effective unit test tools are available both in the public domain or for purchase.
If you are willing to spend the money and the time, tools are available that make build-
ing tests for your application easier. APIs can be tested through white box unit tests,
web interfaces can be tested through robots, and client/server applications can be
tested through front-end testing tools, such as those from Segue (www.segue.com).
7.2.3
Unit tests will limit our flexibility
It seems counterintuitive, but unit tests actually increase your flexibility by allowing you
to accept more risks in refactoring code. If you have thorough unit tests for your mod-
ule, then you can refactor the code any way you please; so long as the unit tests pass,
you can be reasonably sure that the module is still running properly.
Working with unit tests is an immensely gratifying experience. If you have not
worked with a solid unit test framework before, you are missing out on valuable peace
of mind. It will be the end of sleepless nights spent worrying about application sta-
bility or reliability. 
Change paper size in pdf document - Compress reduce PDF size in C#.net, ASP.NET, MVC, Ajax, WinForms, WPF
C# Code & .NET API to Compress & Decompress PDF Document
can a pdf file be compressed; change font size in pdf
Change paper size in pdf document - VB.NET PDF File Compress Library: Compress reduce PDF size in vb.net, ASP.NET, MVC, Ajax, WinForms, WPF
VB.NET PDF Document Compression and Decompression Control SDK
change font size pdf comment box; best pdf compressor online
CASE
STUDY
AUGMENTED
CODE
143
7.2.4
The interface isn’t ready yet
When fully embraced, unit testing inverts the development process. You start by devel-
oping the unit tests, and then implement the interfaces to match the unit test cases.
These tests should cover both normal and error conditions. Once the tests pass, you
can feel confident that the interface is implemented completely.
When you invert the development process to create the unit tests before doing
anything else, the question of whether the interfaces are sufficient for tests becomes a
non-issue.
7.2.5
Unit tests are not worth the time
To understand the value of unit tests, you have to try using them at least once. I can
guarantee that after you have experienced the confidence that comes with writing
code with a unit test running as a bug-catching safety net, you’ll never have to be
convinced again.
7.2.6
Adding unit tests to a legacy system is painful
Let’s face it: Doing almost anything with legacy code is painful. And that pain is mul-
tilayered. First, you have to try to understand the code. Then comes figuring out what
changes need to be made. Finally, there is the fear that new changes will break the code.
This last part is where unit tests pay off. If you had unit tests in the first place, you
wouldn’t be worrying about the ramifications of code changes.
When you are modifying core components of a legacy system, it is worth the time
to integrate unit tests before making the changes. If you have thorough unit tests
and they pass after the modifications, you can be reasonably satisfied that the system
is stable.
7.2.7
Unit tests can only prove the existence of errors
In 1968 Edsger Dijkstra wrote that “Testing can demonstrate the existence of errors, but
never their absence.” The idea is that you should be able to mathematically prove the
validity of an algorithm and thus prove every possible test case. This is both fascinating
and true. If you have algorithmic tests, you may not need unit tests. However, if your
system does not have algorithmic tests you should consider using unit tests as a starting
point to ensure code quality. 
Our case study on unit test generation uses a generator that builds unit tests by
parsing the test data directly from a C implementation file.
7.3
CASE
STUDY
AUGMENTED
CODE
The case study generator allows an engineer to add test cases to C code using specifi-
cally formatted comments. What follows is some C code with test data embedded into
the comments. The placement of the test data is critical. The test data should be
directly in front of the function to which the data will be sent. In this way, you keep the
C# PDF Print Library: Print PDF documents in C#.net, ASP.NET
PDF Document; Remove Password from PDF; Change PDF Permission Settings. orientation, PDF document printing paper size and PDF document printing resolution.
adjusting page size in pdf; change page size pdf acrobat
VB.NET Image: Robust OCR Recognition SDK for VB.NET, .NET Image
into searchable text formats, such as PDF, PDF/A, WORD control; Capable of detecting word, font, line size, location; money in archiving, filing and paper storing
best way to compress pdf file; pdf compression settings
144
CHAPTER 7
G
ENERATING
UNIT
TESTS
test data close to the function and obviate the need to specify to which function the
test data applies.
// <test result="3"><a>1</a><b>2</b></test>                          
// <test result="6"><a>2</a><b>4</b></test>                          
int add( int a, int b ) { return a + b; }     
The function to test
int main( int argc, char *argv[] )
{
run_tests();      
The test invocation
printf( "%d\n", add( 1, 2 ) );
return 0;
}
The output of the generator goes back into the input file. The following code fragment
is the output of the generator using the input file:
// <test result="3"><a>1</a><b>2</b></test>
// <test result="6"><a>2</a><b>4</b></test>
int add( int a, int b ) { return a + b; }
int main( int argc, char *argv[] )
{
run_tests();
printf( "%d\n", add( 1, 2 ) );
return 0;
}
// <test_start>      
The test marker comment
static int run_tests()      
The new unit test function
{
if ( add( 1, 2 ) != 3 )     
A test invocation
{
printf( stderr, "Unit test add_1 failed!\n" );
return 0;
}
if ( add( 2, 4 ) != 6 )
{
printf( stderr, "Unit test add_2 failed!\n" );
return 0;
}
return 1;
}
// </test_start>
In this code, we create a new function called run_tests, which is local to this mod-
ule. This run_tests function calls add twice with the test data specified in the
comments. If the add function does not respond properly, the system prints an error
message and returns zero for failure. If all of the tests run properly, then run_tests
returns 1 for success.
The test data
The generated 
failure handler
CASE
STUDY
AUGMENTED
CODE
145
7.3.1
Roles of the generator
Before you architect a generator for the unit tests, you need to clearly define the role of
the generator in your overall system. First, define which requirements are covered by
the generator and which are not. Let’s start with what is covered:
• Translation of technical unit test requirements into running unit tests (by technical
requirements, we mean the input data for each test as well as the valid output for
each set of input data).
• The framework within which the unit tests will be run.
• The assessment of the success or failure of the unit tests.
The generator does not take responsibility for:
• Designing the tests.
• Generating test data sets to test an arbitrary interface.
• Testing interfaces where the functions need to be called in a specific order.
• Testing object-oriented interfaces.
The limitation of the order dependency in the interface and the testing of object-ori-
ented interfaces are addressed by other unit test generator designs presented in the
sequential test case generator described later in section 7.4.
7.3.2
Laying out the generator architecture
The generator for the augmented C unit test system is based on the mixed-code gener-
ation model (see chapters 2 and 4 for more on mixed-code generation). It takes the C
code as input, adds the test implementations using the test templates, and writes the
new code back into the original file (after making a backup). Figure 7.3 is a block dia-
gram of the generator.
In the next section, we’ll look at a step-by-step view of how this generator operates
as it executes.
Generator
Test
Templates
CCode
Figure 7.3 3 This generator takes C code, which embeds tests, 
implements the tests, and injects them back into the C.
146
CHAPTER 7
G
ENERATING
UNIT
TESTS
7.3.3
Processing flow 
The C unit test generator follows these processing steps:
Reads the C file.
Tokenizes the C.
Parses the C tokens.
Prepares an array to act as a list of tests.
Inspects each function and follow these steps:
Looks at test data in the comments.
Adds the test data to the list of tests.
Uses a template to create the test function with all of the tests.
Adds the function into the original file text.
Makes a backup of the original file.
Writes the output into the original file.
7.3.4
Building the code for the unit test generator
We’re ready to start writing the code for our generator. Our generator uses the
CTokenizer and CLanguageScanner from the language parsing toolkit
discussed in chapter 3. Once the C comments and function prototypes are parsed and
stored in memory, the generator uses the ERb text-template system to build the new
test function. Once the construction of the function is complete, the generator injects
the function back into the original file using a regular expression. The source code for
the C unit test generator is shown in Listing 7.1.
require "ftools"
require "CTokenizer"                
require "CLanguageScanner"          
require "rexml/document"        
Imports the XML reader
require "erb/erb"               
Imports the text-template handler
class PrototypeWithTests < Prototype     
def initialize() 
super() 
@tests = [] 
end
attr_reader :tests   # The array of tests
def add_tests( tests )
tests.each { |test| @tests.push( test ); }
end
end
C unit test generator
Listing 7.1  testgen.rb
Prototype 
subclass for 
the test data
q
CASE
STUDY
AUGMENTED
CODE
147
class TestData                                   
def initialize()                               
@name = ""                                   
@result = ""
@arguments = {}
end
attr_accessor :name      # The name of the test
attr_accessor :result    # The expected result
attr_reader :arguments   # The test data (a hash)
def add_argument( name, value )
@arguments[ name ] = value
end
end
def parse_data( text )                                           
begin                                                      
data = TestData.new()
doc = REXML::Document.new( text )
data.result = doc.root.attributes[ 'result' ]
data.name = doc.root.attributes[ 'name' ] 
doc.root.elements.each { |elem| 
data.add_argument( elem.name, elem.text.strip ) 
}
data
rescue
nil
end 
end
def find_tests( comments )                                     
tests = []                                                   
comments.each { |comment|
found = comment.to_s.scan( /(<test.*?<\/test>)/ )
if ( found )
found.each { |items|
data = parse_data( items[0] )
tests.push( data ) if ( data )
}
end
}
tests
end
def generate_tests( file_name )                                   
fh = File.open( file_name )                                     
c_text = fh.read()                                              
fh.close()                                                      
tokenizer = CTokenizer.new( )                                   
tokenizer.parse( c_text )                                       
Test data class
w
Parses the test data 
from function comments
e
Finds the test data in 
function comments
r
Tokenizes the C
Reads the 
C file
Shows the main 
generator entry point
t
148
CHAPTER 7
G
ENERATING
UNIT
TESTS
languagescanner = CLanguageScanner.new()      
Creates the LanguageScanner
languagescanner.prototypeClass = PrototypeWithTests            
languagescanner.parse( tokenizer.tokens )   
count = 0                                                                          
languagescanner.prototypes.each { |proto|                      
tests = find_tests( proto.comments )                         
proto.add_tests( tests )                                     
index = 1                                                    
proto.tests.each { |item|                                    
name = "#{proto.method_name}_#{index}"                     
item.name = name unless ( item.name )                      
index += 1
count += 1
}
}
prototypes = languagescanner.prototypes                            
erb = ERb.new( File.new( "templates/c.template" ).read )       
template_result = erb.result( binding )                        
template_result = "// <test_start>\n#{template_result}\n// </test_start>"              
File.copy file_name, "#{file_name}.bak"  
if ( c_text =~ /\/\/ <test_start>.*?\/\/ <\/test_start>/m )     
c_text.sub!( /\/\/ <test_start>.*?\/\/ <\/test_start>/m, 
template_result )
else
c_text += template_result                                     
end                                                             
File.open( file_name, "w" ).write( c_text )     
Creates the new file
print "#{file_name}: #{count} tests found\n"    
Tells the user what we did
end
if ARGV[0] 
generate_tests( ARGV[ 0 ] ) 
Sends a command-line argument to the test generator
else
print "Must specify an input C file\n"
end
q
PrototypeWithTests derives from the Prototype class defined in the language
parser toolkit. The class adds an array of tests, which are TestData objects. Adding
the tests to this class allows you to keep the test data in the same object as the function
name and argument information.
w
The TestData class wraps the name of the test, the expected result value, and the
arguments for the function. There is one TestData object for each test, and it is
attached to the PrototypeWithTests object to which the test is applied.
Iterates over 
each prototype
u
Iterates over 
each test
i
Function proto-
type subclass
y
Scans the 
tokens
Puts new code into
the original file
s
Wraps the template
result in a comment
a
Sets up a proto-
types variable 
for the template
o
Runs the template
Backs up 
the file
CASE
STUDY
AUGMENTED
CODE
149
e
parse_data uses the Rexml package to read the XML that defines a single set of test
data. It takes the XML test as input and returns a TestData object.
r
find_tests scans a comment string for all of the XML blocks of test data. The tests
are returned as an array of strings, with each string containing one <test>…</
test> block.
t
generate_tests takes as input the name of the C file and processes the file, look-
ing for tests and rebuilding the file with the test cases integrated. This function is the
main entry point of the generator.
y
By setting the prototypeClass member variable of the LanguageScanner, you
are telling the LanguageScanner to use your class instead of Prototype when
building new prototype objects. Your prototype objects have the tests member, which
stores the array of tests associated with the prototype.
u
After you have scanned the C file, you need to take each prototype and scan the com-
ments for tests. Start by iterating over all the prototypes in the scanner by using the
each
method on the array of prototypes.
i
If tests don’t have names, you create pseudo-random test names using the name of the
function and an ordinal value.
o
Setting prototypes in the local scope to the prototypes array within the Lan-
guageScanner means the template can get to the variable using just prototypes.
The template can get to your local variables because you use the binding method to
give the template access to your locals.
a
This creates the string that contains all the test comment markers and the result of
the template.
s
This code either finds the comments that bracket the test code and replaces them with
the updated code, or if the comments cannot be found, the comments and the tests are
appended to the end of the file.
Next we turn our attention to the template that both builds the tests and the function
that holds the tests. The main body of the template is the run_tests C function.
The template iterates over the prototypes and, within each prototype, it iterates over all
the test data associated with that prototype. It’s within this inner loop that we create the
if statement that invokes the function with the test data and checks the result.
static int run_tests()
{
<%
prototypes.each { |proto|          
Iterates through each prototype
name = proto.method_name
proto.tests.each { |test|        
Iterates through each test
values = [] 
150
CHAPTER 7
G
ENERATING
UNIT
TESTS
proto.arguments.each { |arg|                                   
values.push( test.arguments[ arg.name.to_s ] )               
}
args = values.join( ", " )                                     
result = test.result
test_name = test.name
%>
if ( <%= name %>( <%= args %> ) != <%= result %> )
{
printf( stderr, "Unit test <%= test_name %> failed!" );
return 0;
}
<%
}
}
%>
return 1;
}
7.3.5
Finding tools for test cases 
Unit testing is becoming increasingly popular, thanks to the advent of the XP
programming style and its reliance on unit testing. As such, few unit test generators are
available. At the time of this writing, the only one we could find was the JUnitDoclet
open source generator for Java’s JUnit testing framework (see www.junitdoclet.org/
features.html).
In the next section, we’ll look at a version of this generator that pays attention
to sequence.
7.4
T
ECHNIQUE
THE
ORDERED
TEST
GENERATOR
The case study generator illustrates a test system which works well against an API that
does not require sequential ordering for the tests. But what about testing an API which
requires that the functions or methods be called in a particular sequence—where val-
ues from one call are passed on to subsequent calls? Sequence-based test systems can
also be built using a code generator.
One approach is to use a test data file with an ordered set of data, in which
special mark-up can be used to show where data should be stored in variables and
passed on to subsequent routines. Here is an example of a test data set for a sequential
test generator:
:ret1,add,1,2
5,add,:ret1,2
The file format is a comma-separated file format. The first field is the return value. If
the return value is a number (e.g., 5), then the function should return that value. If the
return value is specified using a :name, then the return value should be stored in a
variable with the specified name.
Orders the arguments 
properly
T
ECHNIQUE
THE
ORDERED
TEST
GENERATOR
151
The second field is the function name. In both cases in the example, the function name
is add. The remaining fields are the arguments to the function.
Here is the code generated for the input file:
// Test 1
int ret1=add(1,2);
// Test 2
if ( add( ret1, 2 ) != 5 ) { return 0; }
To implement the first line of the file, we have run the add function with the argu-
ments 1 and 2. The output of the function is stored in the ret1 variable. The second
line also calls add, but this time it passes in ret1 and the value 2. We then check the
output against the value 5 to make sure we are adding properly.
This is a simplistic example, but it demonstrates the basic technique for building
a sequential test data set and lets you see what the resulting code will look like.
7.4.1
Roles of the generator
The roles of the ordered unit test generator are the same as the roles of the unit test case
study generator, as described in section 7.3.1.
7.4.2
Laying out the generator architecture
To build the test code that fits within the test framework, you’ll use a tier generator
model. We chose the tier model because you are building fully self-reliant code from
an abstract model—the test data set. You take the ordered test data set as
input. Then, using a set of invocation templates, you build the test code, which sits
inside a test framework (e.g., JUnit, CPPUnit, Ruby/Unit). Figure 7.4 shows the block
I/O architecture.
In the next section, we will walk through the steps of this ordered generator as
it executes.
Generator
Invocation
Templates
TestCode
TestData
TestFramework
Figure 7.4
The I/O flow for a sequential 
test generator
Documents you may be interested
Documents you may be interested