Reputation: 13
I'm developing a battleship game in ASP.NET and I'm stucked on a problem with my unit tests with MSTest.
I want to test the creation of each type of boat and verifying that each boat's constructor make the desired boat with the good width, etc. So, I decided to write a general method with the tag [DataTestMethod]. But I don't understand how I can use an object as a parameter.
Here's an example of what I want :
[DataTestMethod]
[DataRow("Aircraft Cruiser", 5, OccupationType.Aircraft, new Aircraft())]
public void CreateAircraft(string description, int width, OccupationType occupationType, Ship resultShip)
{
var expectedShip = new Ship
{
Description = description,
Width = width,
OccupationType = occupationType
};
Assert.AreEqual(expectedShip, resultShip)
}
But it obvliously doesn't work. So I did something like that :
[DataTestMethod]
[DataRow("Aircraft Cruiser", 5, OccupationType.Aircraft, "Aircraft")]
public void CreateAircraft(string description, int width, OccupationType occupationType, string shipType)
{
var expectedShip = new Ship
{
Description = description,
Width = width,
OccupationType = occupationType
};
Ship resultShip = null;
switch (shipType)
{
case "Aircraft":
resultShip = new Aircraft();
break;
}
Assert.AreEqual(expectedShip, resultShip);
}
but I'm sure that's not the most efficent way to do what I want. Have you some idea ?
Many thanks.
Upvotes: 1
Views: 5018
Reputation: 89140
You can pass an object to an MS Test method using the DynamicData
attribute. This specifies a static method that will produce your test data.
This is used on your test method like so:
[TestMethod]
[DynamicData(nameof(GetTestData), DynamicDataSourceType.Method)]
public void CreateAircraft(string description, int width,
OccupationType occupationType, Ship resultShip)
The method to return your test data returns an IEnumerable of object[] and can be implemented like this:
private static IEnumerable<object[]> GetTestData()
{
yield return new object[]
{
"Aircraft Cruiser",
5,
OccupationType.Aircraft,
new Aircraft()
};
yield return new object[]
{
"X-Wing",
6,
OccupationType.StarFighter,
new Aircraft()
};
}
Upvotes: 3
Reputation: 7813
The first sample is simply not possible to do in C#. As per the spec, attributes have to take constant parameters in its constructor/properties, and anything else is forbideen (as attributes are baked in the binary at compile-time). In this case, what makes it fail is the constructor call new Aircraft()
in the attribute, which is a non-constant expression (it causes the constructor of the Aircraft
class to run), so it cannot be used in attributes at all.
As a workaround, a string is a normally good candidate. Note that C#6 introduces the nameof
operator to ease that and provide some compiler support, as in this:
[DataRow("Aircraft Cruiser", 5, OccupationType.Aircraft, nameof(Aircraft))]
As for the method code itself the switch
is an option if you know all possibilities beforehand, but otherwise you need to involve reflection to create the object from the class name.
Upvotes: 0
Reputation: 2089
You are comparing reference types, that doesn't work as you are comparing the reference in memory, and those will not equal. You should override the Equals() function, then use that in your tests.
The Equals function takes in a type, and then you just do comparisons, for example, add this to your Ship class:
public override bool Equals(Ship obj)
{
if (this.Width != obj.Width)
{
return false;
}
return true;
}
Then you just do this in your tests:
Assert.IsTrue(expectedShip.Equals(resultShip))
Upvotes: 0