Friday, October 3, 2014

Object Mother vs Test Data Builders and the With() extension method

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!

2 comments:

  1. 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/

    I also created an OSS library to make it easier/quicker to write test data builders: https://github.com/robdmoore/NTestDataBuilder

    ReplyDelete
  2. chung cư newskyline văn quán
    hateco 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ý

    ReplyDelete