Cannot create Parameterized Test Fixtures with decimals

3,507 views
Skip to first unread message

Inger Marie B Jakobsen

unread,
Mar 8, 2010, 6:53:26 AM3/8/10
to nunit-...@googlegroups.com
I've tried a couple of ways to do this:

[TestFixture(0.25m, 0.75m)]
[TestFixture( 0.1m, 0.2m, 0.3m, 0.2m, 0.15m ), Category("LongRunning")]
[TestFixture(0.11m, 0.12m, 0.13m, 0.14m, 0.21m, 0.111m, 0.011m,
0.008m, 0.06m, 0.1m ), Category("LongRunning")]
public class My_TestFixture
{

public My_TestFixture(decimal[] coveragepercentages)
{
...
}
}

and

[TestFixture(new decimal[2] { 0.25m, 0.75m })]
[TestFixture(new decimal[5] { 0.1m, 0.2m, 0.3m, 0.2m, 0.15m }),
Category("LongRunning")]
[TestFixture(new decimal[10] { 0.11m, 0.12m, 0.13m, 0.14m, 0.21m,
0.111m, 0.011m, 0.008m, 0.06m, 0.1m }), Category("LongRunning")]
public class My_TestFixture
{

public My_TestFixture(decimal[] coveragepercentages)
{
...
}
}

In both examples, I get these errors, when building the testproject:
An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type


If I change decimal to double (and remove the 'm') after each number,
then the error goes away. But I do not want doubles, I want decimals
because of the better precision where 0.5+0.5 actually equals 1 and
not .00000__09

Is there an explanation for this behaviour (that an array of doubles
and an array of decimals are not treated alike) and a way to make it
do, as I want?


thanks,
Inger marie

Inger Marie B Jakobsen

unread,
Mar 8, 2010, 6:56:54 AM3/8/10
to nunit-...@googlegroups.com
I use version 2.5.2.9222 by the way

--ingermarie

cliff

unread,
Mar 8, 2010, 7:17:09 AM3/8/10
to nunit-...@googlegroups.com
you can't use the decimal type in an attribute.  Use double and convert to decimal in the test.

On Mon, Mar 8, 2010 at 5:56 AM, Inger Marie B Jakobsen <i...@brunsgaard.dk> wrote:
I use version 2.5.2.9222 by the way

--ingermarie

--
You received this message because you are subscribed to the Google Groups "NUnit-Discuss" group.
To post to this group, send email to nunit-...@googlegroups.com.
To unsubscribe from this group, send email to nunit-discus...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nunit-discuss?hl=en.




--
thanks

cliff

cliff

unread,
Mar 8, 2010, 7:18:33 AM3/8/10
to nunit-...@googlegroups.com
sorry, just read the rest of your message after my post.  You're going to have to round the double or use strings and then convert.
--
thanks

cliff

Inger Marie B. Jakobsen

unread,
Mar 8, 2010, 7:57:41 AM3/8/10
to nunit-...@googlegroups.com
I did use doublesbefore and that made my tests very ugly (the property
I want to test has type decimal to avoid the floating point precision
problem - this problem means that if I have an expected return value
of 1, I need to test if the result is between 0.000000000009 and
1.000000000001 (precision not precise in this example). Ie I have to
use GreaterThan and LesserThan instead of the AreEqual... this is not
good enough for my calculations).

Why doesn't Unit allow decimals? It's kind of annoying since this
makes my test prone to the floaing point precision problem that made
me want to change the type from double to decimal in the first place.
And I simply don't understand why decimals are treated differently
from doubles, ints and strings in this regard.


I did discover that this creates the same error, too:

[TestCase(-1m)]
[TestCase(0.000000000009m)]
[TestCase(1.000000000001m)]
[TestCase(100m)]


however this creates no error:
[TestCase(-0.000000000001)]
[TestCase(1.000000000001)]
[TestCase(100)]
public void ATest(decimal newCoverage)

so my guess is that somewhere under the hood, NUnit handles types
individually instead of letting .Net do this?

I've decided to use integers as input and then divide the input with
1000m before transfering them to the decimal[] I want to use as
testfixture input. This means my testfixture looks like this:
[TestFixture(new int[2]{ 250, 750 })]
[TestFixture(new int[5]{100, 200, 300, 200, 150 }), Category("LongRunning")]
[TestFixture(new int[10]{110, 120, 130, 140, 210, 111, 11, 8, 60,
100}), Category("LongRunning")]


This way, I don't run into any floating point issues.

Still, I am interested to know why I cannot use decimals. It seems to
me to be a very strange limitation to have.

best regards,
Inger Marie

2010/3/8 cliff <powe...@aol.com>:

cliff

unread,
Mar 8, 2010, 8:48:25 AM3/8/10
to nunit-...@googlegroups.com
It's not an NUnit issue, but a .NET one.  The decimal type is actually a struct and you aren't allowed to "new" object/structs in an attribute. 
 
It's inconvenient, but until there's a new framework version to support this, we're left with hacks.
 
hope this helps
 
cliff

cliff

unread,
Mar 8, 2010, 8:59:14 AM3/8/10
to nunit-...@googlegroups.com
sorry for the multiple posts, clicking send to quickly.  :)
 
I may have to retract part of what i said and add that this could just be an issue of implicit vs. explict casting of types and possible precision loss, but i'm not 100% certain. sorry for the "not-so-detailed-answer", but in general the decimal type and .Net attributes don't mix well.
--
thanks

cliff

Charlie Poole

unread,
Mar 8, 2010, 9:11:34 AM3/8/10
to nunit-...@googlegroups.com
Hi,

As cliff pointed out, this is a .NET limitation. It's simply
not possible to use a decimal as an argument to an attribute.
The message you get is, of course, from the compiler and
not from NUnit.

NUnit tries to help a little here... if you put a double
or a string in the attribute, but the corresponding method
argument is decimal, it will convert for you. In most cases
this works OK.

If it doesn't work in your case, I suggest using TestCaseSource
rather than TestCase.

Charlie

Charlie Poole

unread,
Mar 8, 2010, 9:16:27 AM3/8/10
to nunit-...@googlegroups.com
Correction...

I looked again and see you are using arguments to TestFixture,
not TestCase. I'm not sure that we do the same automatic
conversion for TestFixture as for TestCase. If not, please
file a bug. The intent is that TestFixture should have the
same flexibility as TestCase.

Until it does, converting from a string is probably your
best workaround - even if it's pretty ugly.

Inger Marie B. Jakobsen

unread,
Mar 8, 2010, 1:44:55 PM3/8/10
to nunit-...@googlegroups.com
I still do not understand. Going to both double and decimal definition
in visual studio reveals them both as structs. So why does one work as
argument and the other not? They even implement the same interfaces
(generics aside).

I did use decimal both as TestFixture and TestCase arguments, though
in the case of TestFixture I used an array of decimals (decimal[])
which I simply do not understand why is treated differently than an
array of ints or doubles.

Best regards
Inger Marie

2010/3/8 Charlie Poole <cha...@nunit.com>:

Sune Foldager

unread,
Mar 8, 2010, 2:25:08 PM3/8/10
to nunit-...@googlegroups.com
On 08/03/2010, at 19.44, "Inger Marie B. Jakobsen" <imbrun...@gmail.com
> wrote:

> I still do not understand. Going to both double and decimal definition
> in visual studio reveals them both as structs. So why does one work as
> argument and the other not? They even implement the same interfaces
> (generics aside).


Yes but double is a CLR primitive type, while decimal is not. AFAIK
only primitive types, strings and arrays of those can be used for
attributes.

/Sune

>

Charlie Poole

unread,
Mar 8, 2010, 3:37:00 PM3/8/10
to nunit-...@googlegroups.com
According to the C# language specification...

"The types of positional and named parameters for an attribute class are
limited to the attribute parameter types, which are:

* One of the following types: bool, byte, char, double, float, int,
long, short, string.
* The type object.
* The type System.Type.
* An enum type, provided it has public accessibility and the types in
which it is nested (if any) also have public accessibility (Section 17.2).
* Single-dimensional arrays of the above types."

Under the first bullet, all primitive types are allowed. That includes
double but not decimal.

Good news... you aren't crazy! I think just about everyone who
first encounters this is surprised - I know I was. But decimal
is not a primitive, and so it's not allowed.

Let me know whether automatic conversion from double / string
is working for TestFixtureAttribute. Better yet, please file
a bug if it isn't. Things are confusing enough without NUnit
making it worse by treating arguments differently between
different attribute types.

Inger Marie B. Jakobsen

unread,
Mar 9, 2010, 2:48:16 AM3/9/10
to nunit-...@googlegroups.com
I have decided to work with ints and create decimals by dividing these
ints by 1000. That way I get to work with enough precision and I get
to be sure that Assert.AreEqual(1, 0.5+0.5) does not fail.

When using string[] and object[] I get the same error as when using decimal.

Thanks for your replies.

--ingermarie

2010/3/8 Charlie Poole <cha...@nunit.com>:

Charlie Poole

unread,
Mar 9, 2010, 10:09:50 AM3/9/10
to nunit-...@googlegroups.com
Hi Ingermarie,

> I have decided to work with ints and create decimals by
> dividing these ints by 1000. That way I get to work with
> enough precision and I get to be sure that Assert.AreEqual(1,
> 0.5+0.5) does not fail.

With such low precision, double would most likely work as well.
For TestCase, the conversion you want would be automatic, but
I guess not for TestFixture.



> When using string[] and object[] I get the same error as when
> using decimal.

It would be helpful (prevent us from forgetting) if you could
file a bug that details the specific limitations you found
with each attribute.

Charlie

Inger Marie B. Jakobsen

unread,
Mar 12, 2010, 7:55:45 AM3/12/10
to nunit-...@googlegroups.com
I did have a failing test where 0.5+0.5 failed to be equal to 1. That
was what got me started on the decimals in the first place.

thx for your help
--ingermarie

2010/3/9 Charlie Poole <cha...@nunit.com>:

Richard Howells

unread,
Mar 12, 2010, 8:08:12 AM3/12/10
to nunit-...@googlegroups.com
I think this is a common problem with floating point. It's ALWAYS and
approximation. Comparing floats for equality often reveals tiny
differences.

I THINK you'll find that the AreEqual overloads for float allow you to offer
a tolerance. If your data really wants to be floating point specifying a
tolerance might be your best solution.

Cheers,

Richard

ric...@dynamisys.co.uk
www.dynamisys.co.uk
(L) 01793 731225
(M) 07769 266522
Dynamisys is registered in the UK - number 4152561

Charlie Poole

unread,
Mar 12, 2010, 12:17:58 PM3/12/10
to nunit-...@googlegroups.com
Note that the Within syntax offers a lot more flexibility
than the old approach of adding a tolerance to AreEqual...

Assert.That(actual, Is.EqualTo(expected).Within(.001);
Assert.That(actual, Is.EqualTo(expected).Within(.1).Percent;
Assert.That(actual, Is.EqualTo(expected).Within(1).Ulps;
etc.

Reply all
Reply to author
Forward
0 new messages