Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asserting unverified added properties to an object in a unit test

I have MSTest (VS 2012) unit tests where I want to assert that the various properties of an object have the values I want. There are many ways to do this. However my main concern is that if a new property is added to the object, it's easy to overlook updating the unit tests to make sure that it has the values we expect.

One thing I can think of is to use reflection to enumerate the public properties of the object, then keep track of which properties the unit test has asserted, and at the end, assert if any properties did not get checked.

Has anybody already written something similar?

Any better ideas?

Update: I should point out that the objedct in question is something like a Data Transfer Object where there are other classes/methods that cause data in that object to be updated. It's easy to overlook updating the tests for those classes/methods to make sure that we account for all of the object's properties. I want something a bit stronger (i.e. can't be forgotten or overlooked) than right-clicking on the object, finding references, and reviewing the code.

For example:

public class Person {
  public string FirstName;
}

public Person GetPerson() {}

[TestMethod]
public void GetPerson_ReturnsFilledInPerson()
{
    var actual = target.GetPerson();
    Assert.IsNotNull(actual.FirstName);
    // If somebody later adds LastName to Person, 
    // we want this unit test to fail until the LastName is checked too.
}

Thanks,

Dan

like image 282
Dan C Avatar asked Nov 13 '22 09:11

Dan C


1 Answers

What you have asked for is not unreasonable. For simple Domain class to DTO mapping, you can use a framework such as Automapper. You just tell it domain class A maps to dto ADto and it will use convention over configuration to map the properties. The framework also ships with a way to test that you have done all your mappings. You can call Mapper.AssertConfigurationIsValid(); and it will make sure that every property in the Dto has been mapped to a property in the domain class (either explicity in code or implicity by having the same property names).

So basically you are trying to do the same thing, which is why it is a very valid scenario. Lets say you choose not to go with Automapper. I am guessing you don't have a seperate mapper in your code (in which case you can use reflection to test your mapper class/code). I have done a similar thing where all the DTOs implement a DeepCopy method that returns another IDto. But we want the actual type to be the same as the calling type

    [Test]
    public void DeepCopyReturnsSameTypeAsOriginal()
    {
        var iDtoType = typeof (IDto);
        var allIDtoTypes = iDtoType.Assembly.GetTypes().Where(t => iDtoType.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract).ToList();

        foreach (var currentIDtoType in allIDtoTypes)
        {
            var instanceOfDtoType = (IDto)Activator.CreateInstance(currentIDtoType);
            var deepCopyType = instanceOfDtoType.DeepCopy();
            Assert.AreEqual(instanceOfDtoType.GetType(), deepCopyType.GetType(), 
                                string.Format("Deep Copy of Type '{0}' does not return the same Type. It returns '{1}'. The DeepCopy method of IDto should return the same type.",
                                instanceOfDtoType.GetType(), deepCopyType.GetType()));
        }
    }

You could use similar logic to get your person object. Get all the public properties of person object and iterate over them and assert that the concrete person object you returned contains a non null value for that particular property.

Although in this case the test has to be used only with very specific objects where you know that all properties must have a non null value. With value types for example, if the AccountBalance property returns 0, does the person have 0 balance or is the property not set? If the person has an email property which is null, does that mean it is not mapped or do we just not have the email for the person in the system. Which is why if you have specific mapper class or use Automapper, then you are more specifically testing that mapping exists as opposed to "does this have a value", which as I said may still work with a few limited classes in your application.

Yet another option is to decorate properties you know that can't be null and must be mapped with a custom attribute. For example

Public class PersonDto {
    [TestNotNull]
    public string FirstName { get; set}

}

Then in your unit test you can use reflection to find all properties with this attribute and make sure they are not null. But of course you run into the same problem you are trying to solve at a different level. If your fear is that people will forget to map important fields, there is not guarantee that they will remember to decorate these fields with the custom attribute.

Of course, you might not want to test that the property is being mapped, only that the property exists. In that case use reflection to get a list of all the properties of your domain object type and ensure that each of them exists in the list of properties in your DTO object type. But this doesn't test that the properties themselves are being mapped, only that they exist. If you use Automapper and its AssertConfigurationIsValid() test, you test that each property in your DTO is being mapped properly, not that each property in your domain has an associated property in the DTO.

So to answer your question, if you want to test both that the property exists and that it is being mapped properly, you need two tests. One to compare the domain object type with the Dto object type and ensure the properties of one exist in the other, and another test to make sure that all the properties in the DTO are being mapped to something (regardless of whether you are doing the mapping in your own code or using a framework like Automapper).

like image 76
Chaitanya Avatar answered Nov 14 '22 23:11

Chaitanya