Steve Chambers Steve Chambers - 9 months ago 42
C# Question

Why is a unit test hardware-dependent when encountering Parallel.foreach?

I'm using Moq to unit test some code that includes a


The Arrange phase sets up 4 exceptions to be thrown within the loop and then wrapped in an

This passed on my i7 processor and I checked in the code.

Afterwards, a colleague complained it wasn't passing for him. It turned out
was only spawning 2 threads on his Core2duo before bombing out and hence only 2 exceptions were wrapped in the

The question is what to do about this so the unit test doesn't depend on processor architecture? A couple of thoughts:-

  1. There is a Microsoft article on manually adding exceptions to
    but we're not keen on doing this as the loop
    should exit at the earliest opportunity if there is a problem.

  2. ParallelOptions.MaxDegreeOfParallelism
    put an upper limit on the number of threads used. But unless this is turned down
    to 1 (which seems more like cheating than proper unit testing) how
    can the unit test know how many threads will actually be used and
    hence set up the Arrange and Assert phases propertly?

Answer Source

You should not test something like that - it's implementation detail.

Thing is - Parallel.ForEach will process elements until it gets exception. When exception occurs it will stop processnig any new elements (but will finish processing of the ones currently processed) and then throw AgregateException.

Now - your i7 CPU have 4 cores + Hyper Threading, which results in more threads spawned for processing, thus you can get more exceptions (because for example 4 things can be processed at the same time when exception occurs). But on Core2Duo with just 2 cores, only 2 items will be processed at the same time (it's because TPL is smart enough to create only enough threads for processing, not more than available cores).

Testing that actually 4 exception occured gives you no knowledge. It's machine dependent. You should instead test if at least one exception occured - since that's what you are expecting. If future user will run your code on old single core machine he will recieve just this one exception in AggregateException.

Number of thrown exception is machine specific, like for example calculation time - you would not assert on how long something was calculated, so you should not assert on number of exception in this case.