{"id":1751,"date":"2011-02-08T08:18:51","date_gmt":"2011-02-08T13:18:51","guid":{"rendered":"http:\/\/www.andygibson.net\/blog\/?p=1751"},"modified":"2011-02-08T11:22:39","modified_gmt":"2011-02-08T16:22:39","slug":"generate-test-data-with-datafactory","status":"publish","type":"post","link":"https:\/\/www.andygibson.net\/blog\/article\/generate-test-data-with-datafactory\/","title":{"rendered":"Generate Test Data with DataFactory"},"content":{"rendered":"<p>DataFactory is a project I just released which allows you to easily generate test data. It was primarily written for populating database for dev or test environments by providing values for names, addresses, email addresses, phone numbers, text, and dates.<br \/>\n<!--more--><br \/>\nTo add DataFactory to your maven project, just add it as a dependency in your <code>pom.xml<\/code> file.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;dependency&gt;\r\n\t&lt;groupId&gt;org.fluttercode.datafactory&lt;\/groupId&gt;\r\n\t&lt;artifactId&gt;datafactory&lt;\/artifactId&gt;\r\n\t&lt;version&gt;0.8&lt;\/version&gt;\r\n\t&lt;type&gt;jar&lt;\/type&gt;\r\n&lt;\/dependency&gt;\r\n<\/pre>\n<h1>Generating Test Data<\/h1>\n<p>Now you can create instances of the <code>DataFactory<\/code> class and create data :<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class Main {\r\n\r\n\tpublic static void main(String&#x5B;] args) {\r\n\t\tDataFactory df = new DataFactory();\r\n\t\tfor (int i = 0; i &lt; 100; i++) {\t\t\t\r\n\t\t\tString name = df.getFirstName() + &quot; &quot;+ df.getLastName();\r\n\t\t\tSystem.out.println(name);\r\n\t\t}\r\n\t}\r\n}\r\n<\/pre>\n<p>The produced output is :<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nLindsey Craft\r\nErica Larsen\r\nRyan Levine\r\nErika Smith\r\nBrooklyn Sloan\r\nKaren Mayer\r\nEddie O'neill\r\nNancy Stevens\r\n<\/pre>\n<p>The <code>DataFactory<\/code> class can generate different types of values, from addresses to random text to random dates, to dates within a fixed time period.  Addresses and business names can be created using the following code : <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nDataFactory df = new DataFactory();\r\nfor (int i = 0; i &lt; 100; i++) {\t\t\t\r\n\tString address = df.getAddress()+&quot;,&quot;+df.getCity()+&quot;,&quot;+df.getNumberText(5);\r\n\tString business = df.getBusinessName();\r\n\tSystem.out.println(business + &quot; located at &quot; + address);\r\n}\r\n<\/pre>\n<p>to produce :<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUvalda Signs located at 1383 Beam Way,Lyons,19316\r\nAlma Accounting located at 1386 Countiss St,Nashville,14967\r\nFort Stewart Engineering located at 1753 Bethesda Rd,Springfield,26306\r\nSugar Hill Textiles located at 1141 Loudon Circle,Cordele,83937\r\nAlbany Engineering located at 1185 Grieves Avenue,Sugar Hill,36753\r\nPoulan Insurance located at 816 Cohen Blvd,Lake City,74839\r\nCrescent Services located at 1085 Cloveridge Boulevard,Bemiss,08769\r\n<\/pre>\n<h1>Dates<\/h1>\n<p>There are a number of features to create dates, the first being creating a random date which is usually in a given sensible date range. <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nDataFactory df = new DataFactory();\r\nDate minDate = df.getDate(2000, 1, 1);\r\nDate maxDate = new Date();\r\nfor (int i = 0; i &lt; 10; i++) {\r\n\tDate start = df.getDateBetween(minDate, maxDate);\r\n\tSystem.out.println(&quot;Date = &quot;+start);\r\n}\r\n<\/pre>\n<p>This produces a list of random dates between 1\/1\/2000 and the current date. Typically, a random date might be constrained by some other date, for example you can&#8217;t have an end date that occurs before the start date. In this case, you would plug the start date in as the minimum date value :<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nDataFactory df = new DataFactory();\r\nDate minDate = df.getDate(2000, 1, 1);\r\nDate maxDate = new Date();\r\n\t\t\r\nfor (int i = 0; i &lt; 10; i++) {\r\n\tDate start = df.getDateBetween(minDate, maxDate);\r\n\tDate end = df.getDateBetween(start, maxDate);\r\n\tSystem.out.println(&quot;Date range = &quot; + dateToString(start) + &quot; to &quot; + dateToString(end));\r\n}\r\n<\/pre>\n<p>The result is a list of dates where the second date is always later than the first : <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nDate range = 04\/29\/2005 to 07\/16\/2006\r\nDate range = 08\/07\/2009 to 01\/19\/2010\r\nDate range = 09\/22\/2000 to 12\/15\/2003\r\nDate range = 07\/31\/2004 to 03\/24\/2009\r\nDate range = 06\/27\/2003 to 01\/10\/2007\r\nDate range = 07\/10\/2003 to 04\/02\/2008\r\nDate range = 01\/04\/2003 to 01\/12\/2005\r\n<\/pre>\n<p>In many cases, you might want your end date to be only within a few days of the start date. For example, helpdesk support tickets or hotel stays don&#8217;t last for years. To do this, you can specify the number of days from the base date you want to generate a result. In this case, we make the end date within 10 days of the begin date : <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nfor (int i = 0; i &lt; 10; i++) {\r\n\tDate start = df.getDateBetween(minDate, maxDate);\r\n\tDate end = df.getDate(start, 0, 10); \/\/set end to within 10 days of the start\r\n\tSystem.out.println(&quot;Date range = &quot; + dateToString(start) + &quot; to &quot; + dateToString(end));\r\n}\r\n<\/pre>\n<p>And the result : <\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nDate range = 04\/29\/2005 to 04\/30\/2005\r\nDate range = 12\/29\/2003 to 12\/30\/2003\r\nDate range = 06\/25\/2003 to 07\/03\/2003\r\nDate range = 10\/19\/2009 to 10\/19\/2009\r\n<\/pre>\n<p>You can also specify a negative minimum days value that could return a date prior to the base date or a positive minimum date value to get a later date. Here&#8217;s a more complex example that uses different date rules to come up with some complex test data.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nfor (int i = 0; i &lt; 10; i++) {\r\n\t \/\/generate an order date\r\n\tDate orderDate = df.getDateBetween(minDate, maxDate);\r\n\r\n\t\/\/estimate delivery 4-10 days after ordering\r\n\tDate estimatedDeliveryDate = df.getDate(orderDate, 4, 10);\r\n\r\n\t\/\/deliver between 2 days prior and 3 days after delivery estimate\r\n\tDate actualDeliveryDate = df.getDate(estimatedDeliveryDate, -2, 3); \r\n\r\n\tString msg =  &quot;Ordered on &quot;+dateToString(orderDate) +\r\n\t\t&quot; deliver by = &quot;+dateToString(estimatedDeliveryDate)+\r\n\t\t&quot; delivered on &quot; + dateToString(actualDeliveryDate);\t\t\t\t\t\r\n\r\n\tif (estimatedDeliveryDate.before(actualDeliveryDate)) {\r\n\t\tmsg = msg + &quot; - LATE&quot;;\r\n\t}\r\n\tif (estimatedDeliveryDate.after(actualDeliveryDate)) {\r\n\t\tmsg = msg + &quot; - EARLY&quot;;\r\n\t}\r\n\tSystem.out.println(msg);\r\n}\r\n<\/pre>\n<p>Here we calculate an order date, and create a delivery date that is at least 4 days out but no more than 10, and then we created an actual delivery date that is between 2 days prior and 3 days after the expected delivery date.<br \/>\nNotice how we cherry picked the dates, the estimated delivery date is at least 4 days out from the order date, and the actual delivery date will only be at most 2 days prior to the estimated date. This means the actual delivery date is always at least 2 days out from the order date and we won&#8217;t get a delivery date value that is before the item was ordered. This code produces the following values : <\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nOrdered on 04\/29\/2005 deliver by = 05\/06\/2005 delivered on 05\/06\/2005\r\nOrdered on 08\/07\/2009 deliver by = 08\/13\/2009 delivered on 08\/13\/2009\r\nOrdered on 09\/22\/2000 deliver by = 09\/27\/2000 delivered on 09\/25\/2000 - EARLY\r\nOrdered on 07\/31\/2004 deliver by = 08\/07\/2004 delivered on 08\/09\/2004 - LATE\r\nOrdered on 06\/27\/2003 deliver by = 07\/04\/2003 delivered on 07\/04\/2003\r\nOrdered on 07\/10\/2003 deliver by = 07\/19\/2003 delivered on 07\/18\/2003 - EARLY\r\nOrdered on 01\/04\/2003 deliver by = 01\/08\/2003 delivered on 01\/08\/2003\r\n<\/pre>\n<h1>Custom Random Values<\/h1>\n<p>If there is a set of values that is very specific to your application that you might want to generate data from, you can use methods on the <code>DataFactory<\/code> class to return values with the option for it to be randomly be a default value.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\tpublic static void main(String&#x5B;] args) {\r\n\t\tDataFactory df = new DataFactory();\r\n\r\n\t\t\/\/favorite animal\r\n\t\tString&#x5B;] values = {&quot;Cat&quot;,&quot;Dog&quot;,&quot;Goat&quot;,&quot;Horse&quot;,&quot;Sheep&quot;};\r\n\t\tfor (int i = 0; i &lt; 100; i++) {\t\t\t\r\n\t\t\tSystem.out.println(df.getItem(values,80,&quot;None&quot;));\r\n\t\t}\t\r\n\t}\r\n<\/pre>\n<p>This example uses the array of animals and returns a value with a 20% chance of  being the default value of &#8220;None&#8221; to produce the following : <\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nSheep\r\nNone\r\nDog\r\nHorse\r\n<\/pre>\n<h1>Textual Data<\/h1>\n<p>Random text data comes in two forms, absolutely random data and text data made up of words. You can generate either using the following methods : <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nDataFactory df = new DataFactory();\r\nSystem.out.println(df.getRandomText(20, 25));\r\nSystem.out.println(df.getRandomChars(20));\r\nSystem.out.println(df.getRandomWord(4, 10))\r\n<\/pre>\n<p>which produces <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nbadly numbers good hot I\r\nywyypgqorighfawpftjq\r\ndemanded\r\n<\/pre>\n<p>All three of these methods can be passed a single length which returns a fixed length string, or a min\/max length which produces a random string with a length somewhere between the min\/max. For the single word method, if there are no words in the dictionary of suitable length, then a word is generated using random characters. <\/p>\n<h1>Changing the test data values produced<\/h1>\n<p>The data used to generate the values come from classes that can be replaced with other versions. For example, the name values can be changed by providing the <code>DataFactory<\/code> instance with an object that implements the <code>NameDataValues<\/code> interface. Here is a simple class that does that to return Scandinavian first names and delegates to the the default implementation to return all the other values.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class ScandinavianNames  implements NameDataValues {\r\n\r\n\t\/\/first name values to use\r\n\tString&#x5B;] firstNames = {&quot;Anders&quot;,&quot;Freyd\u00eds&quot;,&quot;Gerlach&quot;,&quot;Sigdis&quot;};\r\n\r\n\t\/\/delegate to the default implementation for the other values\r\n\tNameDataValues defaults = new DefaultNameDataValues();\r\n\t\r\n\tpublic String&#x5B;] getFirstNames() {\r\n\t\t\/\/return our custom list of names\r\n\t\treturn firstNames;\r\n\t}\r\n\r\n\t\/\/for the other values, just use the defaults\r\n\tpublic String&#x5B;] getLastNames() {\r\n\t\treturn defaults.getLastNames();\r\n\t}\r\n\r\n\tpublic String&#x5B;] getPrefixes() {\r\n\t\treturn defaults.getPrefixes();\r\n\t}\r\n\r\n\tpublic String&#x5B;] getSuffixes() {\r\n\t\treturn defaults.getSuffixes();\r\n\t}\r\n\r\n}\r\n\r\n<\/pre>\n<p>Obviously, to use all your own names you would add and return values for last name and the suffix\/prefix values. To use this new implementation, just create an instance of the data provider and pass it to the instance of the data factory. <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic static void main(String&#x5B;] args) {\r\n\tDataFactory df = new DataFactory();\r\n\tdf.setNameDataValues(new ScandinavianNames());\r\n\tfor (int i = 0; i &lt; 10; i++) {\r\n\t\tSystem.out.println(df.getName());\r\n\t}\r\n}\r\n<\/pre>\n<p>Our results are <\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nSigdis Craft\r\nGerlach Larsen\r\nSigdis Levine\r\nSigdis Smith\r\nFreyd\u00eds Sloan\r\nGerlach Mayer\r\n<\/pre>\n<p>You can always start working with the default implementation and use a more locale specific implementation if you need it later.<\/p>\n<p>The different pieces that can be replaced are as follows : <\/p>\n<ul>\n<li><code>NameDataValues<\/code> &#8211; Generates names and suffix\/prefixes<\/li>\n<li><code>ContentDataValues.java<\/code> &#8211; Generates words, business types, email domain names and top level domain values<\/li>\n<li><code>AddressDataValues<\/code> &#8211; Generates city names, street names and address suffixes<\/li>\n<\/ul>\n<p>Note that if you intend on replacing the component that generates words, you should have a good collection of words of various lengths from 2 up to say 8 or more characters. <\/p>\n<p>Hopefully this will give you a head start in generating data in development and test environments for new projects. Now I have DataFactory in the Central Maven Repository I plan on using this in the Knappsack archetypes rather than hard coding the data which was in fact generated from an earlier DataFactory implementation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>DataFactory is a project I just released which allows you to easily generate test data. It was primarily written for populating database for dev or test environments by providing values for names, addresses, email addresses, phone numbers, text, and dates.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[63],"tags":[105],"_links":{"self":[{"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1751"}],"collection":[{"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/comments?post=1751"}],"version-history":[{"count":4,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1751\/revisions"}],"predecessor-version":[{"id":1754,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1751\/revisions\/1754"}],"wp:attachment":[{"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/media?parent=1751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/categories?post=1751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/tags?post=1751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}