Michael Schnerring
Michael Schnerring

Reputation: 3661

Abstract way to create an illegal parameter class for unit testing

The data fields of our column based database are mapped into a DataField class. On each data field object the GetValue<T>() method can be invoked.

If T is an illegal type an exception gets thrown. What type should I pass in my unit test, where I test if an exception gets thrown if I pass an illegal type? The next known illegal type which comes in my mind? Or is there an approach with some more abstraction?

public object GetValue<T>()
{
    if (typeof(T) == typeof(string)) return ValueString;
    if (typeof(T) == typeof(int?)) return ValueInt;
    if (typeof(T) == typeof(double?)) return ValueDouble;
    if (typeof(T) == typeof(DateTime?)) return ValueDateTime;
    if (typeof(T) == typeof(bool)) return ValueBoolean == true;

    var ex = new Exception("Following type is not supported: " + typeof(T));
    Log.Error(ex);
    throw ex;
}

So every type but those should throw this exception if they get passed. So I'd need a kind of dummy type, right?

At the moment my unit test looks like this:

[Fact]
public void If_T_is_illegal_type_an_exception_gets_thrown()
{
    _dataField = new DataField(_params);
    Assert.Throws<Exception>(() => _dataField.GetValue<Type>());
}

Upvotes: 1

Views: 90

Answers (3)

AlanT
AlanT

Reputation: 3663

As mentioned above, one test for each supported type and one for a non-supported type should suffice.

The one thing I do note though is that the exception is not the only expected result for the error. How are you verifying that the error is logged?
Do you have a way of doing this?
If not, do you want/need one?

Also, a totally unsolicited code review point... why

if (typeof(T) == typeof(bool)) return ValueBoolean == true; 

instead of

if (typeof(T) == typeof(bool)) return ValueBoolean; 

Alan.

Upvotes: 0

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236218

Currently if you call GetValue<bool>() your code will call typeof(T) five times and make 5 comparisons. Then it will return boxed boolean value as object. And the worst thing is that signature of this method says nothing about which types are allowed and which will throw exception. Can I call GetValue<decimal>()? Don't know. I should try and have runtime exception if it is not allowed.

Consider creating overloaded methods for required types:

bool GetBooleanValue()
decimal GetDecimalValue()

This describes perfectly what types it could return. There are no long if chain inside. Caller will not receive objects. And you will have test for each of this methods without questions.

Upvotes: 0

yamen
yamen

Reputation: 15618

Remember that unit testing is trying to get through all code paths and ensuring correct behaviour. You should have 6 tests in total: one for each of the 5 valid types, and one with any other type (as you currently have) to cover the final code path. Not sure why you'd need something more abstract.

You may prefer to use explicit conversions and casting so that this becomes a compile time test rather than run time: http://msdn.microsoft.com/en-us/library/xhbhezf4(v=vs.100).aspx

Upvotes: 2

Related Questions