Writing unit tests for lambda functions in C#

758 views
Skip to first unread message

Luca Minudel

unread,
Aug 21, 2011, 8:59:32 AM8/21/11
to Growing Object-Oriented Software

Recently I wrote unit tests for code using lambda functions and
composition of functions.
I would say I had to unit test code written with a functional
programming paradigm, even if not with a pure functional language. I
tried to use what I've learned of the GOOS style.



Here follow some notes from this experience.


a) Initially I noticed that some unit tests for a function
(calculating the census representativity quotas of a population by one
criteria, say gender) were testing similar things the unit tests for
another function (calculating the census representativity quotas by
age-group criteria) were already testing .

Because of this, after investigating the code I noticed the existence
of a basic function that could be extracted and reused/composed
(calculating size of the parts of a partition for a given initial
population size and a given partitioning criteria/function). In other
words I learned that the functions initially under tests (gender/age-
group partitioning) were violating the SRP and I extracted one
responsibility in a new function (generic partitioning function).
So after solving the SRP violation I could move the similar almost
duplicated unit tests (for the partition criteria gender/age-group)
against the extracted responsibility (the generic partitioning) and so
eliminate the tests duplication.


b) After this when a new representativity criteria was implemented
(calculating the census representativity quota also by region) I
noticed that I still had to add unit tests for the combination of
partitions (gender and region, age-group and region, ...) and the
different order the partitioning were applied (commutativity).

Here I recognized a mistake similar to the one I made when I started
unit testing OO code: the unit tests were not really 'unit' , instead
were testing many objects in the same test. While here I was testing
many functions composed together in the same test and this caused the
combinatorial explosion of unit test cases.

So I tried to applied the GOOS stile with mock/stub and try to test
one function at the time (for example the one calculating the
partition for a specific census criteria tested separately from the
calculation of the generic set partitioning). I end up mocking/
stubbing one function at the time.

While doing this I noticed that the responsibilities were not properly
organized/grouped/split (in the value object representing the sample
with a representativity quota, and in the functions partitioning by
criteria and in the generic partitioning function).
I was able to organize the responsibilities properly and so then unit
test one function at time mocking or stubbing the composed functions
as needed.

When mocking I noticed it worked well for me to mock one function at
the time (this means 1 interface for 1 function, remember that I'm
using lambda from C# an OO language with functional extensions, not a
pure functional language).
With stub I noticed that worked well for me to pass an "identity"
function as stub (well where the co-domain is not the same of the
domain there cannot really be and 'identity' function, but guess you
still get the idea of what I mean).




Does this make sense / sounds right ? Would you suggest/are you using
other approaches ? How are you dealing with similar problems ?
Curios to ear and learn more


Luca
http://blogs.ugidotnet.org/luka/



Luca
http://blogs.ugidotnet.org/luka/

Steve Freeman

unread,
Aug 22, 2011, 2:29:31 PM8/22/11
to growing-object-o...@googlegroups.com
A nice story. That's what I would expect to happen. I've always thought of mocks as representing some kind of partial function. The use of identity functions as stubs is an interesting trick.

Presumably, there are some higher-level tests to show that everything holds together.

Perhaps you could write this up with code examples?

S

Steve Freeman

Winner of the Agile Alliance Gordon Pask award 2006
Book: http://www.growing-object-oriented-software.com

+44 797 179 4105
Twitter: @sf105
Higher Order Logic Limited
Registered office. 2 Church Street, Burnham, Bucks, SL1 7HZ.
Company registered in England & Wales. Number 7522677

Luca Minudel

unread,
Aug 25, 2011, 1:20:01 PM8/25/11
to Growing Object-Oriented Software
On Aug 22, 8:29 pm, Steve Freeman <steve.free...@m3p.co.uk> wrote:
>
> ...
>
> Presumably, there are some higher-level tests to show that everything holds together.
>

Yes there are acceptance tests that tests the census representativity.
Those green tests have been useful to see that all the parts were
working well together as expected also when refactoring this code and
moving responsibilities around between classes.

> Perhaps you could write this up with code examples?
>

Here it is, a small functions composition, still it was not easy to
move away from the initial unit tests that were not really unit and
find another good enough direction.


This was the starting point: http://www.pastie.org/2427601
The RepresentativeSample class that have a PanellistsCount property
(here is important only the number of panelists, every single panelist
item/entity is of no interest here) and the representativity criteria
of that sample that can be by gender, by age group or by main region.
Note the ? in the declaration of the properties, in C# it means that
the property can be null, it means that the sample has not been
partitioned with that criteria.

Look at the functions CreatePartitionByAgeGroup(),
CreatePartitionByGender() and CreatePartitionByMainRegions(): they all
call the private CreatePartition() function passing a
PartitionFactoryFunction as argument. This is a simple function
composition.
The unit tests that I initially wrote on CreatePartitionByAgeGroup()
are very similar to the one on CreatePartitionByGender() and on
CreatePartitionByMainRegions(). They are somehow all testing the
CreatePartition() function and how it is used. So they do not look
very "unit" to me.




Because of this I tried to extract CreatePartition() function,
initially in another class as a public instance function and try to
test it separately from RepresentativeSample.
The challenge here was that the CreatePartition() function accept as
argument another function. A simple function composition, not common
in OO code, instead something common in functional code.
So here is how I extracted CreatePartition() function:
http://www.pastie.org/2427676
Note that:
- the class and the function is generic to avoid the dependency to
RepresentativeSample,
- the class implements one interface and the function is implemented
by an instance method, this enable me to mock it when I test the
interaction between RepresentativeSample and the Create function.
- the delegate PartitionPartFactoryFunction is not needed/mandatory
(the Funct actions and lambda expressions in C# work well without it,
still to me it help to better describe with names the expected
function parameter signature. Maybe is just because I'm not enaugh
into the functional paradigm, time will tell.
After extracting the Partition function here is how
RepesentativeSample looks like: http://www.pastie.org/2427723
The partition functions CreatePartitionByAgeGroup(),
CreatePartitionByGender() and CreatePartitionByMainRegions() now
delegate the partition to the extracted CreatePartition() function
(now Partition<T>.Create).

The new/interesting part for me is in the unit tests of the
interaction between RepesentativeSample and the CreatePartition()
function (now Partition<T>.Create). Is the test with a mock of the
function call and also of the functions composition, that looks to me
to be something related to the functional paradigm: http://www.pastie.org/2427792
Note that the mock MockPartitionBuilder here is a custom hand-written
mock that expect the function call (to Create) and also that expect
that the function passed by RepesentativeSample as argument for
PartitionFactoryFunction behave as expected (this is specified by the
expectedPartitionFactoryFunctionResult value).





And finally these are the tests for the extracted CreatePartition()
function (now Partition<T>.Create) :
http://www.pastie.org/2428047
Tests are about the expected results of the partitioning and the
commutativity property that a partitioning function should have.
Note:
- at the end of the file the "identity" function that is passed as a
stub to the Create function (was CreatePartition) to easily check the
use/composition of the passed function work as expected
- initially these tests needed to be repeated for all the 3 methods
CreatePartitionByAgeGroup(), CreatePartitionByGender() and
CreatePartitionByMainRegions(), now this is no more needed
- also the test of the commutativity property required to test all the
combination of the 3 functions now this is done only once for
Partition<T>.Create. A sign that now responsibilities are better
assigned/organized.


I had other similar refactoring and unit test/mocking approach on
other classes related to the same feature and it worked equally well
there for me. The 3 partition functions defined in RepesentativeSample
at the end were moved in the RepesentativeSamples class that compute
the partition both for a single sample and for a set of samples (can
compute the partition of another partition) so RepesentativeSample end
up to be a simple read-only value-object.




Any comment/feedback/suggestion is welcome.

Luca
Reply all
Reply to author
Forward
0 new messages