// This helper method starts with just a single parameter:
Company company = newCompany(PUBLIC);
// But soon it acquires more and more parameters.
// Conditionals creep into the newCompany() method body to handle the nulls,
// and the method calls become hard to read due to the long parameter lists:
Company small = newCompany(2, 2, null, PUBLIC);
Company privatelyOwned = newCompany(null, null, null, PRIVATE);
Company bankrupt = newCompany(null, null, PAST_DATE, PUBLIC);
// Or a new method is added each time a test needs a different combination of fields:
Company small = newCompanyWithEmployeesAndBoardMembers(2, 2, PUBLIC);
Company privatelyOwned = newCompanyWithType(PRIVATE);
Company bankrupt = newCompanyWithBankruptcyDate(PAST_DATE, PUBLIC);
|
So, we don't set defaults? Your advice seems contradictory there.
ReplyDeleteNo. Set defaults, but make your test set those same values if it cares about that particular value. It's about making your test inputs easy to create but not relying on that state in the test itself. Basically, you should be able to change the defaults at any time for any reason to anything and your tests should still pass.
ReplyDeleteI think it would be better not setting defaults in order to force developers to explicitly set values in their tests.
DeleteWe don't want to make our tests robust, but our production code, if you know what I mean...
It seems valid to initialize the object with sensible values and then explicitly re-declare the things you care about in the body of the test.
ReplyDeleteDo you have recommendations on techniques for handling the case of inadvertent inclusion? Ie, the test inadvertently becomes dependent on a default unintentionally. ("oh yeah, this is always initialized to , so ...")
Perhaps some sort of "change every default/inverse" antimatter test object to validate you're not depending on the defaults. Bonus would be getting to include "antiMatter[inputName]" as test code.
What we do is create defaults that would normally be 'good' results or uninteresting golden path tests, and then allow tests to add nondefaults when they want to test the variation of the behavior. This allows for the tests to be very clear on what is changing and why with a minimum of repetition. You can easily see what input is changing and what matters.
DeleteI think the point here is if we have defaults that influence the test outcome we should at least make this visible to the testing framework users. Not simply hiding it under the hood.
ReplyDeleteE.g. Lets say that Company behavior is influenced by its number of employees (small, medium, big, being small less than 6).
You could then include some knowledge in the method names of where you create the builder.
private static Company.Builder newCompanyForSmallNumberOfEmpoyees() {
return Company.newBuilder().setType(PUBLIC).setEmployees(5); // Set required fields
}
On usage:
Company smallCompany = newCompanyForSmallNumberOfEmpoyees().build();
What would you recommend in case of Kotlin's default parameters?
ReplyDeleteIs having build pattern still beneficial?