Olivier Delierre
Olivier Delierre

Reputation: 13

Passing object in MSTest method parameters

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

Answers (3)

Richard Garside
Richard Garside

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

Alejandro
Alejandro

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

raterus
raterus

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.

.Net Equals Function

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

Related Questions