When working with unit tests, you often have the need to create ‘valid’ data objects to work with. For example,
It’s often a bad idea write out the object creation and initialization in your unit test.For example:
[TestMethod]
public void TestSomething()
{
var customerToInsert = new Customer{
Name = "a name",
Address = "an address",
..
25thProperty = "a value"
};
}
Why do I think this is often a bad idea? Well for one reason, you have to put a lot of lines of code in your test that are likely not relevant to the test itself. But there is a more important reason. When you change your logic so that the definition of a valid customer changes, all of a sudden ALL unit tests that use a customer may or may not fail, depending on how they have been instantiated.
To avoid that situation, most people find it useful to define a sort of object factory. Now you have a single point in your testcode to define what consitutes a valid object. If the definition of what’s valid changes, you only have to change a single place.
In general, i’ve seen two competing patterns to help with this challenge:
The difference between these patterns is: a mother creates a valid object immediately. If you want to alter the creation process in specific situations, you’ll have to create overloads. If you require many variations, this can lead to many overloads as well.
So, many people prefer the TestDataBuilder pattern over the Object Mother pattern, because it gives you much more fine grained control over these variations. And I agree that it’s not preffered to create many overloads on your object mothers for all variations.
However, creating a test data builder is just SOO much work. So I prefer a simple solution, object mothers with the .With() extension method.
The With() extension method
I typically use a simple extension method for simple customizations:
ObjectMother.BuildCustomer().With(x => x.Name = “the name”);
Simple, clear and just as expressive as a builder method. Here’s the extension method:
public static TSubject With<TSubject, TReturnValue>
(this TSubject subject, Func<TSubject, TReturnValue> func)
{
func(subject);
return subject;
}
This simple extension method allows me to have a single place where I have the definition of a ‘valid’ object: Within the mother. And still I can do customizations without having to create many different overloads.
But what about more complex customizations?
Well.. I didn’t say this exension method is the ONLY extension method you could use. How difficult do you think it would be to create an extension method that would do more complex initialization?
ObjectMother.BuildCustomer()
.FromFrance() // Builds a french address and phone number
.ThatIsUnderAge() // set's the birth date to a specific validation rule
.With(x => x.Name="erwin")
By Chaining these extension methods together, you can easily create the object you desire. If you find yourself doing the same set of customizations several times, then (and only then) is it worth creating a separate method for it.
If you need an object graph.. it’s not difficult to chain multiple object mothers together internally. The FromFrance() method above could call another ObjectMother method to create and customize a valid address. etc..
I think this pattern strikes a nice balance between the flexibility of a test data builder with the low entry bar of an object mother.
Building Object Mothers with AutoFixture
Lastly, I just want to call out AutoFixture, AutoFixture is a generic implementation of the Test Data Builder pattern. And it’s great!
While it’s not suited for creating all types of objects (I’ve had some issues with complex WCF messages), it really saves a lot of effort when you just want to create an object with ‘some’ data.
So most of the time, I hide AutoFixture within my Object Mothers.
public static class EntityMother
{
private static Fixture fixture = new Fixture();
public static Customer BuildACustomer()
{
return fixture.Create<Customer>();
}
}
It saves me a lot of effort in creating manual object graphs, but still provides me a clear and single point where my test data is being generated.
Hope you find this useful!
You should check out the stuff I've posted about combining Object Mothers and Test Data Builders: http://robdmoore.id.au/blog/2013/05/26/test-data-generation-the-right-way-object-mother-test-data-builders-nsubstitute-nbuilder/
ReplyDeleteI also created an OSS library to make it easier/quicker to write test data builders: https://github.com/robdmoore/NTestDataBuilder
chung cư newskyline văn quán
ReplyDeletehateco hoàng mai
chung cư hà nội
dịch vụ hoàn thuế
dịch vụ kế toán thuế ở bên ngoài thúc đẩy sản sinh thêm mấy lần thậm chí mấy chục lần tốc độ
khôi phục, mà người chết thì không có chức năng của sinh mệnh.
Bất quá, điều này đối với Đoạn Vân mà nói cũng không thành vấn đề. Đoạn
Vân có thể lợi dụng phương pháp châm cứu của Trung y làm cho các chức
năng của Hi Nhã đạt được sự khôi phục tạm thời, cũng là một sự khôi phục
giả tạo. Cái này giống như trên người của một con ếch đã chết, sau khi
kích thích hệ thần kinh của nó, hệ thần kinh của con ếch vẫn sẽ có phản
ứng giống như trước.
Nói thật, nếu không phải thân thể Hi Nhã kịp thời được đặt vào bên trong
Không gian giới tử được miễn dịch hoàn toàn, sẽ không biến chất này, thì
Đoạn Vân tuyệt đối không có khả năng biện pháp nào. Nhưng may mắn là Hi
Nhã chỉ mất đi tất cả dấu hiệu của sinh cơ, ngoại trừ trái tim thì các
chức năng có thể coi như hoàn hảo không khuyết tật.
Lấy ra các loại dụng cụ phẫu thuật, Đoạn Vân liền mở lồng ngực mỹ nhân
đang ngủ ra. "Oa! Tên gia hỏa ấy không biết thương hoa tiếc ngọc hay
sao? Một cô gái xinh đẹp như vậy lại một kiếm xuyên phá ngay bên trái
đôi ngọc thố hoàn mỹ như vậy! Có lẽ là bà tám rồi, chứ nam nhân ai lại
độc ác như vậy!" Đoạn Vân nhè nhẹ vuốt ve ngọc thố bên đó,một bên tính
toán khai đao ở chỗ nào tốt nhất, một bên không khỏi đối với hung thủ
kia có chút thành kiến.
dịch vụ quyết toán thuế
khoá học kế toán thuế
trung tâm kế toán
dịch vụ báo cáo thuế hàng tháng
dich vu ke toan noi bo
dịch vụ dọn dẹp sổ sách
kế toán cho giám đốc quản lý
Adore how magnificently every word is composed with appropriate adjust.
ReplyDeletelinkedin