This is the last part of an article series exploring reading and writing CSV files with C#/.NET.
Part 1 covered converting the contents of a DataTable into CSV format. Part 2 explained how to read a CSV file back into a DataTable.
Finally we will look at some test cases for use with NUnit, a very important tool that I'll describe in more detail in an upcoming article. There is quite a bit of code, but it is all very simple and easy to understand. Each method marked with [Test] will be called by NUnit and run one of the CSV related methods in turn. Assert statements will then check if the returned values are valid or not.
First, let's check the parser methods. There are several tests here that all test something different; they all pass a CSV formatted string to the CsvParser.Parse method and check the returned DataTable.
using System;
using System.
Data;
using System.
IO;
using NUnit.
Framework;
namespace CsvParser
{
[TestFixture]
public class TestCsvParser
{
[Test]
public void UnquotedLine()
{
DataTable table = CsvParser.Parse("one,two,three");
Assert.IsNotNull(table);
Assert.AreEqual(3, table.Columns.Count);
Assert.AreEqual(1, table.Rows.Count);
Assert.AreEqual("one", table.Rows[0][0]);
Assert.AreEqual("two", table.Rows[0][1]);
Assert.AreEqual("three", table.Rows[0][2]);
}
[Test]
public void QuotedLine()
{
DataTable table = CsvParser.Parse("\"one\",\"two\",\"three\"");
Assert.IsNotNull(table);
Assert.AreEqual(3, table.Columns.Count);
Assert.AreEqual(1, table.Rows.Count);
Assert.AreEqual("one", table.Rows[0][0]);
Assert.AreEqual("two", table.Rows[0][1]);
Assert.AreEqual("three", table.Rows[0][2]);
}
[Test]
public void TwoLines()
{
DataTable table = CsvParser.Parse("one,two,three\nfour,five,six\n");
Assert.IsNotNull(table);
Assert.AreEqual(3, table.Columns.Count);
Assert.AreEqual(2, table.Rows.Count);
Assert.AreEqual("one", table.Rows[0][0]);
Assert.AreEqual("two", table.Rows[0][1]);
Assert.AreEqual("three", table.Rows[0][2]);
Assert.AreEqual("four", table.Rows[1][0]);
Assert.AreEqual("five", table.Rows[1][1]);
Assert.AreEqual("six", table.Rows[1][2]);
}
[Test]
public void QuotedMultilineValue()
{
DataTable table = CsvParser.Parse(
"\"one\n\"\"beer\"\"\",\"\"\"two\"\"\nbeers\",\"three\nbeers\"");
Assert.IsNotNull(table);
Assert.AreEqual(3, table.Columns.Count);
Assert.AreEqual(1, table.Rows.Count);
Assert.AreEqual("one\n\"beer\"", table.Rows[0][0]);
Assert.AreEqual("\"two\"\nbeers", table.Rows[0][1]);
Assert.AreEqual("three\nbeers", table.Rows[0][2]);
}
[Test]
public void Headers()
{
DataTable table = CsvParser.Parse(
"First,Last,Email\nAndreas,Knab,knabar@yahoo.com", true);
Assert.IsNotNull(table);
Assert.AreEqual(3, table.Columns.Count);
Assert.AreEqual(1, table.Rows.Count);
Assert.AreEqual("Andreas", table.Rows[0]["First"]);
Assert.AreEqual("Knab", table.Rows[0]["Last"]);
Assert.AreEqual("knabar@yahoo.com", table.Rows[0]["Email"]);
}
[Test]
public void TrailingNewlines()
{
DataTable table = CsvParser.Parse("test\n\n\n\n\n\n");
Assert.IsNotNull(table);
Assert.AreEqual(1, table.Columns.Count);
Assert.AreEqual(6, table.Rows.Count);
Assert.AreEqual("test", table.Rows[0][0]);
}
[Test]
public void Newlines()
{
DataTable table = CsvParser.Parse("test1\x0Atest2\x0Dtest3\x0D\x0Atest4");
Assert.IsNotNull(table);
Assert.AreEqual(1, table.Columns.Count);
Assert.AreEqual(4, table.Rows.Count);
Assert.AreEqual("test1", table.Rows[0][0]);
Assert.AreEqual("test2", table.Rows[1][0]);
Assert.AreEqual("test3", table.Rows[2][0]);
Assert.AreEqual("test4", table.Rows[3][0]);
}
}
}
Second, the opposite route – testing the CsvWriter. Since it takes quite a bit of effort to completely populate a DataTable, I'll cheat and create the DataTable using the CsvParser, which has been tested separately and is known to work.
using System;
using System.
Data;
using System.
IO;
using NUnit.
Framework;
using CsvParser;
namespace CsvWriter
{
[TestFixture]
public class TestCsvWriter
{
[Test]
public void PlainText()
{
string s = "one,two,three\n";
DataTable table = CsvParser.Parse(s);
string t = CsvWriter.WriteToString(table, false, false);
Assert.AreEqual(s, t);
}
[Test]
public void QuotedText()
{
string s = "\"one\",\"two\",\"three\"\n";
DataTable table = CsvParser.Parse(s);
string t = CsvWriter.WriteToString(table, false, true);
Assert.AreEqual(s, t);
}
[Test]
public void MultiLineText()
{
string s = "\"one\nline\",\"two\nline\",\"three\nline\"\n";
DataTable table = CsvParser.Parse(s);
string t = CsvWriter.WriteToString(table, false, false);
Assert.AreEqual(s, t);
}
[Test]
public void Headers()
{
string s = "First,Last,Email\nAndreas,Knab,knabar@yahoo.com\n";
DataTable table = CsvParser.Parse(s, true);
string t = CsvWriter.WriteToString(table, true, false);
Assert.AreEqual(s, t);
}
}
}
And that's it! There are more special cases that could be tested, especially invalid input like non-CSV files, but technically everything can be interpreted as CSV, so the question would be what output to expect.
As I mentioned earlier, NUnit is worth a closer look, so check back for more.