Here's a simple CSV tester that sees whether a given CSV module can parse and produce CSV that contains some of the more common features. I've also included a sample MyCSV.pm that uses Text::CSV_XS. The basic idea of the test is that you start with a CSV string, parse that into a table of rows and columns as an AoA; test the AoA to see if it has the right values in the right places; then turn the AoA back into a CSV string (i.e. turn the table's rows and columns into CSV) and test it against the original string.

The example should produce a table of two rows with three columns each. The second column of the first row contains embedded quotes, commas, and newlines. The second column of the second row is NULL (i.e. undef).

update : added tests for rows and columns

# csv_test.pl
#!/usr/bin/perl -w use strict; use Test::More tests => 5; use MyCSV; my $old_csv = qq{5,"a, ""b""\n and c",6\n7,,8\n}; my $table = MyCSV::parse_csv( $old_csv ); my $new_csv = MyCSV::produce_csv( $table ); ok( @$table == 2 , 'correctly finds two rows' ); ok( (@{$table->[0]} == 3 and @{$table->[1]} == 3) , 'correctly finds three columns in each row' ); ok( $table->[0]->[1] eq qq{a, "b"\n and c} , 'supports parsing embedded comma, newline, quotes' ); ok( !$table->[1]->[1] , 'supports parsing NULLs' ); ok( $new_csv eq $old_csv , 'supports producing embedded comma, newline, quotes; NULLs' ); __END__
# MyCSV.pm (using Text::CSV_XS)
################ package MyCSV; ################ use Text::CSV_XS; use IO::Scalar; # parse_csv() # # receives a CSV string # returns a table of rows and column as an AoA # sub parse_csv { my($input_str)=@_; my $aoa = []; my $csv = Text::CSV_XS->new({binary=>1}); my $fh = IO::Scalar->new(\$input_str); while (my $cols = $csv->getline($fh)) { last unless @$cols; push @$aoa,$cols } return $aoa } # produce_csv() # # receives a table of rows and columns as an AoA # returns a CSV string # sub produce_csv { my($aoa)=@_; my $output_str = ''; my $csv = Text::CSV_XS->new({binary=>1}); for my $row( @$aoa ) { @$row = map {defined $_ ? $_ : ''} @$row; my $success = $csv->combine(@$row); die "Coulnd't parse '@$row'\n" unless $success; $output_str .= $csv->string . $/; } return $output_str; } 1;