Reputation: 11763
Let's assume I have a class with some similar properties:
public string First { get; set; }
public string Second { get; set; }
public string Third { get; set; }
I want to test them in the same way in my tests... So I write:
[Test]
public void TestFirst()
{
// Asserting strings here
}
Is there a way to avoid creating three Tests (one for First, one for Second and one for Third)?
I'm looking for something like [Values(First, Second, Third)]
, so i can then write one test that will iterate through the properties.
Cheers, and thanks in advance :)
Upvotes: 3
Views: 479
Reputation: 11763
Thanks all for your answers and help. Learned plenty of things.
Here's what I've ended up doing. I've used reflection to get all the string properties, and then set to a value, check value is set, set to null, check to see it returns an empty string (logic in property's getter).
[Test]
public void Test_AllStringProperties()
{
// Linq query to get a list containing all string properties
var string_props= (from prop in bkvm.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where
prop.PropertyType == typeof(string) &&
prop.CanWrite && prop.CanRead
select prop).ToList();
string_props.ForEach(p =>{
// Set value of property to a different string
string set_val = string.Format("Setting [{0}] to: \"Testing string\".", p.Name);
p.SetValue(bkvm, "Testing string", null);
Debug.WriteLine(set_val);
// Assert it was set correctly
Assert.AreEqual("Testing string", p.GetValue(bkvm, null));
// Set property to null
p.SetValue(bkvm,null,null);
set_val = string.Format("Setting [{0}] to null. Should yield an empty string.", p.Name);
Debug.WriteLine(set_val);
// Assert it returns an empty string.
Assert.AreEqual(string.Empty,p.GetValue(bkvm, null));
}
);
}
This way I don't need to be concerned if someone adds a property, since it'll be checked automatically, without me needing to update the test code (as you might guess, not everybody updates nor writes tests :)
Any comments on this solution will be welcomed.
Upvotes: 0
Reputation: 13681
It's easy enough to do this but I question whether it's worth it.
How To - many of the above answers work, but this seems simplest, assuming you are testing a newly created object...
[TestCase("First", "foo"]
[TestCase("Second", 42]
[TestCase("Third", 3.14]
public void MyTest(string name, object expected)
{
Assert.That(new MyClass(), Has.Property(name).EqualTo(expected));
}
However, putting three separate asserts in the test seems much easier to read...
[Test]
public void MyTest()
{
var testObject = new MyClass();
Assert.That(testObject, Has.Property("First").EqualTo("foo"));
Assert.That(testObject, Has.Property("Second").EqualTo(42));
Assert.That(testObject, Has.Property("Third").EqualTo(3.14));
}
This assumes, of course, that the three asserts are all part of testing one thing, like DefaultConstructorInitializesMyClassCorrectly. If that's not what you're testing, then three tests make more sense, even though it requires more typing. One way to be sure is to see if you are able to come up with a reasonable name for the test.
Charlie
Upvotes: 1
Reputation: 6408
You can write parameterized test and pass property accessors as parameters:
See example: Assume your class with 3 properties is:
public class MyClass
{
public string First { get; set; }
public string Second { get; set; }
public string Third { get; set; }
}
Then test might look:
[TestFixture]
public class MyTest
{
private TestCaseData[] propertyCases = new[]
{
new TestCaseData(
"First",
(Func<MyClass, string>) (obj => obj.First),
(Action<MyClass, string>) ((obj, newVal) => obj.First = newVal)),
new TestCaseData(
"Second",
(Func<MyClass, string>) (obj => obj.Second),
(Action<MyClass, string>) ((obj, newVal) => obj.Second = newVal)),
new TestCaseData(
"Third",
(Func<MyClass, string>) (obj => obj.Third),
(Action<MyClass, string>) ((obj, newVal) => obj.Third = newVal))
};
[Test]
[TestCaseSource("propertyCases")]
public void Test(string description, Func<MyClass, string> getter, Action<MyClass, string> setter)
{
var obj = new MyClass();
setter(obj, "42");
var actual = getter(obj);
Assert.That(actual, Is.EqualTo("42"));
}
}
Few notes:
1. Unused string description is passed as 1-st parameter to distinguish test cases when they are run via NUnit test runner UI or via Resharper.
2. Tet cases are independent and even when test for First
property fails the other 2 tests will be run.
3. It is possible to run only one test case separately via NUnit test runner UI or via Resharper.
So, your test is clean and DRY :)
Upvotes: 0
Reputation: 8669
There are a lot of possibilities to express such kind of assertions using NUnit.Framework.Constraints.Constraint
. In addition, instead of using ValuesAttribute
or TestCaseAttribute
you could describe more inputs for tests using ValuesSoueceAttribute
or TestCaseSourceAttribute
Let's define expected properties names and their values using TestCaseSourceAttribute
public IEnumerable TestCasesSourcesAllProperties
{
get
{
yield return new TestCaseData(
new Tuple<string, string>[] {
Tuple.Create("First", "foo"),
Tuple.Create("Second", "bar"),
Tuple.Create("Third", "boo") } as object)
.SetDescription("Test all properties using Constraint expression");
}
}
Now we could build constraint for all three properties in a single test
// get test parameters from TestCasesSourcesAllProperties
[TestCaseSource("TestCasesSourcesAllProperties")]
public void ClassUnderTest_CheckAllProperty_ExpectValues(Tuple<string, string>[] propertiesNamesWithValues)
{
// Arrange
ClassUnderTest cut = null;
// Act: perform actual test, here is only assignment
cut = new ClassUnderTest { First = "foo", Second = "bar", Third = "boo" };
// Assert
// check that class-under-test is not null
NUnit.Framework.Constraints.Constraint expression = Is.Not.Null;
foreach(var property in propertiesNamesWithValues)
{
// add constraint for every property one by one
expression = expression.And.Property(property.Item1).EqualTo(property.Item2);
}
Assert.That(cut, expression);
}
Here is a full example
Confition logic, i.e.foreach
, inside test logic
Upvotes: 1
Reputation: 15971
You should be able to use expression trees for this purpose. Using the MSDN documentation for the Expression.Property method I have created the following helper method for obtaining a type T
property named propertyName
from an arbitrary object obj
:
public T InvokePropertyExpression<T>(object obj, string propertyName)
{
return Expression.Lambda<Func<T>>(Expression.Property(
Expression.Constant(obj), propertyName)).Compile()();
}
Using this helper method in my unit test, I can now access the relevant property based on its name, for example like this:
[Test, Sequential]
public void Tests([Values("First", "Second", "Third")] string propertyName,
[Values("hello", "again", "you")] string expected)
{
var obj = new SomeClass
{ First = "hello", Second = "again", Third = "you" };
var actual = InvokePropertyExpression<string>(obj, propertyName);
Assert.AreEqual(expected, actual);
}
Upvotes: 1
Reputation: 7463
You can use the Values
attribute on a parameter to the test method:
[Test]
public void MyTest([Values("A","B")] string s)
{
...
}
However, this will only work for string constants (i.e. not values of properties).
I guess you could use reflection to get the values of the properties from the given values, e.g.
[Test]
public void MyTest([Values("A","B")] string propName)
{
var myClass = new MyClass();
var value = myClass.GetType().GetProperty(propName).GetValue(myClass, null);
// test value
}
But that's not exactly the cleanest solution. Perhaps you could write a test which calls a method to test each property.
[Test]
public void MyTest()
{
var myClass = new MyClass();
MyPropertyTest(myClass.First);
MyPropertyTest(myClass.Second);
MyPropertyTest(myClass.Third);
}
public void MyPropertyTest(string value)
{
// Assert on string
}
However, it's best to avoid this way of testing, as a unit test should do just that - test a unit of your code. If each test is named correctly, it can serve to keep a record of just what you are expecting, and can be easily added to in future.
Upvotes: 0
Reputation: 340
How about this:
[TestFixture]
public class Tests
{
[Test]
public void Test()
{
var obj = new MyClass();
obj.First = "some value";
obj.Second = "some value";
obj.Third = "some value";
AssertPropertyValues(obj, "some value", x => x.First, x => x.Second, x => x.Third);
}
private void AssertPropertyValues<T, TProp>(T obj, TProp expectedValue, params Func<T, TProp>[] properties)
{
foreach (var property in properties)
{
TProp actualValue = property(obj);
Assert.AreEqual(expectedValue, actualValue);
}
}
}
Upvotes: 2