There're several ways to generate data for tests (not only unit tests), for example, Object M开发者_如何学运维other, builders, etc. Another useful approach is to write test data as plain text:
product: Main; prices: 145, 255; Expire: 10-Apr-2011; qty: 2; includes: Sub product: Sub; prices: 145, 255; Expire: 10-Apr-2011; qty: 2
and then parse it into C# objects. This is easy to use in unit tests (because deep inner collections can be written in single line), this is even more convenient to use in FitNesse-like system (because this DSL naturally fits into wiki), and so on.
So I use this and write parser, but it's tedious to write each time. I'm not a big expert in DSL/language parsers, but I think they can help here. What would be the right one to use? I only heard about:
- DSL (I mean, any DSL)
- Boo (that I think can do DSL)
- ANTLR
but I don't even know which one to pick and where to start.
So the question: is it reasonable to use some kind of DSL to generate test data? What would you suggest to do so? Are there any existing cases?
Update: seems like I was not clear enough. It's not about raw string to object convertion. Look at first line and relate it to
var main = Product.New("Main")
.AddPrice(Price.New(145).WithType(PriceType.Main).AndQty(2))
.AddPrice(Price.New(255).WithType(PriceType.Maintenance).AndQty(2))
.Expiration(new DateTime(10, 04, 2011));
var sub = Product
.New("Sub").Parent(main)
.AddPrice(...));
main.AddSubProduct(sub);
products.Add(main);
products.Add(sub);
And note that I first create sub product and then add it to main, even though it is listed in reverse order. Prices are handled in a special way. I want to specify name of Sub product and get reference to it - created. I want to list all product properties - FLAT and NON-REPEATATIVE - on single line. I want to use defaults for properties. And so on.
Update: I'm not convinced to avoid DSL because all the alternative examples are too verbose and not user-friendly. And no-one said anything useful about DSL.
For the data DSL YAML is an excellent candidate. Here is a sample from Wikipedia:
---
receipt: Oz-Ware Purchase Invoice
date: 2007-08-06
customer:
given: Dorothy
family: Gale
items:
- part_no: A4786
descrip: Water Bucket (Filled)
price: 1.47
quantity: 4
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
price: 100.27
quantity: 1
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Westville
state: KS
ship-to: *id001
specialDelivery: >
Follow the Yellow Brick
Road to the Emerald City.
Pay no attention to the
man behind the curtain.
I used YAML in several projects and happy with it.
However, if we are talking about unit-tests it is usually simpler and more readable to construct necessary objects “by hand” with constructors and property assignments in-place. This is because unit-test are by their nature highly focused on some code (unit), and it shouldn't be hard to create data infrastructure that is just enough for the test. It is OK to operate on half-complete entities in unit-tests, don't bother with constructing data that is not related to this concrete test.
For functional tests YAML is great.
I would first start by seeing if my language of choice was rich enough to build my DSL. C# ought to handle your case quite easily:
Product[] products = new Product[] {
new TestProduct{product="Main", prices=new[]{145, 255}, Expire="10-Apr-2011", qty=2, includes="Sub"},
new TestProduct{product="Sub", prices=new[]{145, 255}, Expire="10-Apr-2011", qty=2}
};
Not quite as pretty, but certainly tolerable enough that I would struggle to justify the extra effort of a custom DSL.
Also note that Expire is initialised with a string, but it is obviously a date. This is perfectly reasonable for a DSL idiom, since TestProduct.Expire
's setter can do the translation.
For creating an external DSL I would recommend Eclipse TMF Xtext which is really good (based on ANTLR but simpler), but built on top of Eclipse and Java, however you can generate any code. When it comes to creating testing data, I was inspired by the way the Ruby on Rails guys do it, which was YAML fixtures as mentioned in another answer, but I also saw an approach using factories, which can help you to get rid of some duplicity and inflexibility. Look at this Railscasts 158: Factories not Fixtures, it might give you some ideas for designing the DSL.
精彩评论