BookRepository: The Mock Data Repository for Testing and Demos

Update: I recently added persistence features to the BookRepository. Check out: The Illusion of Persistence: Saving Test Data

My schedule is packed lately with writing test beds and sample code for presentations. One of the problems developers face when creating code that is turned over to a third party is to answer the question: what do you do about a data source? Over the years we’ve seen efforts like Northwind, AdventureWorks and even recently the NotNorthwind movement crop up in order to create an easy-to-use relational database tests and demos. Often, however, developers just need data and shouldn’t be bothered with connection strings and other sorts of database setup.

Bring On the Data

The BookRepository "solution" is a simple mock object and repository class that makes it easy to generate data.

The Entity

The entity object itself is compact and has a number of features to make your life easier. For instance:

  • Factory method for easy creation of objects: Title, Author and Url are based on the ID of the object while Price and PublishDate are values generated with random values.

  • Common formatting: Formatted properties for Price and PublishDate that make it easy to display these typed properties to the user.

  • Ready to use anywhere: Marked as Serializable so you can pass between layers and persist as you wish.

Here’s the code:

[Serializable]
public class Book
{
public int ID { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Url { get; set; }
public double Price { get; set; }
public DateTime PublishDate { get; set; }

public string PriceFormatted
{
get { return string.Format("{0:C}", Price); }
}

public string PublishDateShort
{
get { return PublishDate.ToShortDateString(); }
}

public Book() { }

public static Book Create(int id)
{
Book bk = new Book();
Random random = new Random();

bk.ID = id;
bk.Title = string.Format("Title of Book {0}", id);
bk.Author = string.Format("Author {0}", id);
bk.Url = string.Format("http://site.com/book/{0}/", id);
bk.Price = Convert.ToDouble(string.Format("{0:##.##}", random.Next(5, 35) + random.NextDouble()));
bk.PublishDate = Convert.ToDateTime(string.Format("{0}/{1}/{2}", random.Next(1, 12), random.Next(1, 28), random.Next(1990, DateTime.Now.Year)));

return bk;
}
}

The Repository

The repository class features a number of methods that make generating any amount of data simple. The following is a breakdown of the methods:

  • GetByID: Returns an instance of Book by calling the Book.Create factory method and passing in the given ID.

  • Insert and Update: Build to play nice with the object data source. These methods have parameters for each of the properties. The body of these methods make a call to Debug.WriteLine so you have a natural place to add a breakpoint.

  • Delete: Pass in an ID value to simulate deleting from the persistence layer. This method also calls Debug.WriteLine.

  • GetBooks: Overloaded method that will return a collection of objects as List<Book>. The default parameter will return 10 instances, while you can also pass in a value to numberOfBooks which will generate as many items in the collection as you wish.

  • GetBooksAsDataSet: Overloaded and functions the same way as GetBooks, but instead of a collection of strongly typed objects you get back a DataSet.

The repository also features:

  • Static instance property: A static "instance" property that gets its value from the static constructor. This makes you life easy so when using the object directly you don’t have to "new up" an object each time. Note: While this property may look like an implementation of a singleton – it’s not. A true singleton would have checking to make sure it’s the only instance.

  • DatObjectMethod attributes: There are attributes applied to each of the public method that make recognition by the ObjectDataSource easy.

Here is the code:

public class BookRepository
{
private static BookRepository _instance;

public static BookRepository Instance
{
get { return _instance; }
}

public BookRepository() { }

static BookRepository()
{
_instance = new BookRepository();
}

public Book GetByID(int id)
{
return Book.Create(id);
}

/// <summary>
/// Returns a List of 10 Book objects
/// </summary>
[DataObjectMethod(DataObjectMethodType.Select, true)]
public List<Book> GetBooks()
{
return this.GetBooks(10);
}

/// <summary>
/// Mocks insertion into a persistence layer
/// </summary>
[DataObjectMethod(DataObjectMethodType.Insert, true)]
public void Insert(string title, string author, string url, double price, DateTime publishDate)
{
Debug.WriteLine("Insert object into database");
}

/// <summary>
/// Mocks update to a persistence layer
/// </summary>
[DataObjectMethod(DataObjectMethodType.Update, true)]
public void Update(int ID, string title, string author, string url, double price, DateTime publishDate)
{
Debug.WriteLine("Update database");
}

/// <summary>
/// Mocks deleting from a persistence layer
/// </summary>
[DataObjectMethod(DataObjectMethodType.Delete, true)]
public void Delete(int ID)
{
Debug.WriteLine("Delete from database");
}

/// <summary>
/// Returns a list of Book objects where you decide the quantity
/// </summary>
[DataObjectMethod(DataObjectMethodType.Select, false)]
public List<Book> GetBooks(int numberOfBooks)
{
List<Book> books = new List<Book>();

for (int i = 1; i < (numberOfBooks + 1); i++)
{
books.Add(Book.Create(i));
}

return books;
}

/// <summary>
/// Returns 10 book records in a DataSet
/// </summary>
[DataObjectMethod(DataObjectMethodType.Select, true)]
public DataSet GetBooksAsDataSet()
{
return this.GetBooksAsDataSet(10);
}

/// <summary>
/// Returns a series of books in a DataSet
/// </summary>
[DataObjectMethod(DataObjectMethodType.Select, false)]
public DataSet GetBooksAsDataSet(int numberOfBooks)
{
DataSet ds = new DataSet();
DataTable dt;

dt = new DataTable();
dt.Columns.Add(new DataColumn("ID"));
dt.Columns.Add(new DataColumn("Title"));
dt.Columns.Add(new DataColumn("Author"));
dt.Columns.Add(new DataColumn("Price"));
dt.Columns.Add(new DataColumn("Url"));
dt.Columns.Add(new DataColumn("PublishDate"));

for (int i = 1; i < (numberOfBooks + 1); i++)
{
this.AddNewRow(ref dt, i);
}

ds.Tables.Add(dt);
return ds;
}

private void AddNewRow(ref DataTable dt, int index)
{
DataRow dr = dt.NewRow();
Book bk = Book.Create(index);
dr["ID"] = index;
dr["Title"] = bk.Title;
dr["Author"] = bk.Author;
dr["Price"] = bk.Price;
dr["Url"] = bk.Url;
dr["PublishDate"] = bk.PublishDate;
dt.Rows.Add(dr);
}
}

Buyer Beware

Make no mistake this approach is not meant to replace a relational database if in-fact that is what you need. The purpose of the BookRepository is to have a simple, portable way of generating data and mocking interaction with a persistence layer – enjoy!

What’s Missing?

So far what’s here is proven to be a huge time saver for the type of work I am doing. If you think of anything else that would be useful, please comment below!

Use in Your Presentations

If you would like to use the BookRepository in a presentation and want an easy link to give to your audience, you can use this URL:

http://tinyurl.com/bookrepos

Download

You can download the entire file here.

No Comments