Showing posts with label Unit Testing. Show all posts
Showing posts with label Unit Testing. Show all posts

Wednesday, April 27, 2022

Using AutoFixture To Create Early Bound Entities

@AutoFixtureAutoFixture is an open source library that is used in testing to create objects without having to explicit set all the values.  I recently attempted to use it in a unit test to create an instance of an early bound entity, and assumed it would be extremely trivial, but boy was a wrong.  But now at least, you have the “joy” of reading this blog post about it.




The Problem(s)

This is what attempting to use an AutoFixture straight out of the box to create an entity looks like:

[TestMethod]
public void EarlyBoundAutoFixture_Should_Generate()
{   
    var fixture = new Fixture();
    // Fails here:
    // AutoFixture.ObjectCreationExceptionWithPath: AutoFixture was unable to create an instance from System.Runtime.Serialization.ExtensionDataObject,
    // most likely because it has no public constructor, is an abstract or non-public type.
    var contact = fixture.Create<Contact>();
    Assert.IsNotNull(contact.FirstName);
}

The error basically AutoFixture can’t create the ExtensionDataObject since it does not expose a public constructor.  OK, makes sense.  The simplest thing to do is to make a fluent build call and skip the property, but this doesn’t work because other types like Money, have the ExtensionData property and it will fail for those properties as well, and manually skip the ExtensionData property on every object would make AutoFixture viturally worthless.  The solution is to create an ISpecimenBuilder that tells AutoFixture how to create an ExtensionData (in actuality don’t, just set it to null).  This looks like this:

[TestMethod]
public void EarlyBoundAutoFixture_Should_Generate()
{
    var fixture = new Fixture();
    fixture.Customizations.Add(new SkipExtensionData());
   
    // New error
    // AutoFixture.ObjectCreationExceptionWithPath: AutoFixture was unable to create an instance of type AutoFixture.Kernel.FiniteSequenceRequest
    // because the traversed object graph contains a circular reference. Information about the circular path follows below. This is the correct
    // behavior when a Fixture is equipped with a ThrowingRecursionBehavior, which is the default. This ensures that you are being made aware of
    // circular references in your code. Your first reaction should be to redesign your API in order to get rid of all circular references.
    // However, if this is not possible (most likely because parts or all of the API is delivered by a third party), you can replace this default
    // behavior with a different behavior: on the Fixture instance, remove the ThrowingRecursionBehavior from Fixture.Behaviors, and instead add
    // an instance of OmitOnRecursionBehavior:
    //
    //   fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
    //       .ForEach(b => fixture.Behaviors.Remove(b));
    //   fixture.Behaviors.Add(new OmitOnRecursionBehavior());
    var contact = fixture.Create<Contact>();     Assert.IsNotNull(contact.FirstName);
}


public class SkipExtensionData : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;
        if (pi == null)
        {
            return new NoSpecimen();
        }

        if (typeof(ExtensionDataObject).IsAssignableFrom(pi.PropertyType))
        {
            return null;
        }

        return new NoSpecimen();
    }
}

But once again, a new error is generated.  This time a circular reference error.  Extra points to the team at AutoFixture for putting the solution to the issue in the code. But after adding it, more issues still pop up.

[TestMethod]
public void EarlyBoundAutoFixture_Should_Generate()
{
    var fixture = new Fixture();
    fixture.Customizations.Add(new SkipExtensionData());
    fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
        .ForEach(b => fixture.Behaviors.Remove(b));
    fixture.Behaviors.Add(new OmitOnRecursionBehavior());

    // Yet another error:
    // System.InvalidOperationException: Sequence contains no elements
    // Stack Trace:
    //   Enumerable.First[TSource](IEnumerable`1 source)
    //   Entity.SetRelatedEntities[TEntity](String relationshipSchemaName, Nullable`1 primaryEntityRole, IEnumerable`1 entities)
    //   Contact.set_ReferencedContact_Customer_Contacts(IEnumerable`1 value) line 6219
    var contact = fixture.Create<Contact>();

    Assert.IsNotNull(contact.FirstName);
}

This is a fun error where when setting a related entity collection to an empty collection, you get a Sequence contains no elements error.  (Which I could possible handle in the Early Bound Generator I guess) but this calls out something that in my opinion shouldn’t be getting populated, child collections of entities.  Only the properties of the entity that are actual properties and not LINQ relationships needs to be populated, so we can actually remove the recursive behavior check and resolve this final issue by tweaking the ISpecimenBuilder to skip these types of properties, which brings us to the first solution that doesn’t throw an exception:

[TestMethod]
public void EarlyBoundAutoFixture_Should_Generate()
{
    var fixture = new Fixture();
    fixture.Customizations.Add(new SkipEntityProperties());

    var contact = fixture.Create<Contact>();

    // Fails!  FirstName is Null
    Assert.IsNotNull(contact.FirstName);
}

public class SkipEntityProperties: ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;
        if (pi == null)
        {
            return new NoSpecimen();
        }

        if (typeof(ExtensionDataObject).IsAssignableFrom(pi.PropertyType))
        {
            return null;
        }

        if (pi.DeclaringType == typeof(Entity))
        {
            return null;
        }

        // Property is for an Entity Class, and the Property has a generic type parameter that is an entity, or is an entity
        if (typeof(Entity).IsAssignableFrom(pi.DeclaringType)
            &&
            (pi.PropertyType.IsGenericType && pi.PropertyType.GenericTypeArguments.Any(t => typeof(Entity).IsAssignableFrom(t))
             || typeof(Entity).IsAssignableFrom(pi.PropertyType)
             )
           )
        {
            return null;
        }

        return new NoSpecimen();
    }
}

It was at this point that I couldn’t understand what was going on.  Why aren’t these values getting populated?  2 hours of debugging latter I finally realized that AutoFixture was setting the AttributeCollection of the Entity to null, effectively removing all other variables that were just being set by AutoFixture.  Some more internet researching later I discovered that there was an OmitSpecimen value that would leave the value untouched!  Armed this this knowledge the final solution presented itself!

The Solution

This final bit of code will correctly populate the attributes of the early bound entity:

[TestMethod]
public void EarlyBoundAutoFixture_Should_Generate()
{
    var fixture = new Fixture();
    fixture.Customizations.Add(new SkipEntityProperties());

    var contact = fixture.Create<Contact>();

    Assert.IsNotNull(contact.FirstName);
}

public class SkipEntityProperties: ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;
        if (pi == null)
        {
            return new NoSpecimen();
        }

        if (typeof(ExtensionDataObject).IsAssignableFrom(pi.PropertyType))
        {
            return new OmitSpecimen();
        }

        if (pi.DeclaringType == typeof(Entity))
        {
            return new OmitSpecimen();
        }

        // Property is for an Entity Class, and the Property has a generic type parameter that is an entity, or is an entity
        if (typeof(Entity).IsAssignableFrom(pi.DeclaringType)
            &&
            (pi.PropertyType.IsGenericType && pi.PropertyType.GenericTypeArguments.Any(t => typeof(Entity).IsAssignableFrom(t))
             || typeof(Entity).IsAssignableFrom(pi.PropertyType)
             || typeof(AttributeCollection).IsAssignableFrom(pi.PropertyType)
             )
           )
        {
            return new OmitSpecimen();
        }

        return new NoSpecimen();
    }
}

Here is an example screen shot from above:
image

Notice how everything except the AccountId (Since it’s readonly) has been automatically populated with a default value?  It’s a beautiful thing!

If you found this help, please share it!

Friday, August 4, 2017

TypeScript Makes Writing Cleaner Code Easier

screenshot_thumb3

Clean Coders know that Boolean Arguments can be dangerous and as a general rule, should be avoided.  I had this thought in mind when writing a TypeScript unit test recently.  I had this bit of code which is rather ugly and not immediately obvious what it was doing.
getPaymentMethodsSpy.and.callFake(() => ActionsStubs.GetPaymentMethods.fakeResponseWithPaymentCount(1));
await Payment.setPayments("");
CommonLib.setValue(Payment.fields.paymentMethod, 1);
The fakeResponseWithPaymentCount sets the getPaymentMethodsSpy to generate the given number of fake existing payments for a count (1 in this case) as a response from the custom action, GetPaymentMethods.  Each odd numbered payment is a bank, and each even numbered one is a credit card.   So for this snippet, line 1 fakes a response of 1 Bank Payment, line 2 sets the Payment on the form, and line 3 selects that payment as the payment to use.  Simple right?  Not.  Good luck remembering this and re-creating it for processing a Credit Card.
So how does TypeScript here help make this cleaner?  Before we get there, lets do the obvious first step and refactor these statements out to a method:

async function selectExistingPayment(isBank: boolean) {
    getPaymentMethodsSpy.and.callFake(() => ActionsStubs.GetPaymentMethods.fakeResponseWithPaymentCount(2));
    await Payment.setPayments("");
    CommonLib.setValue(Payment.fields.paymentMethod, isBank ? 1 : 2);
}
This is way more usable, except when I’m looking at a calls site for it, it’s still really not clear:
selectExistingPayment(true);
selectExistingPayment(false);
What does false mean?  Don’t select it?  Sure, you could create an enum and define “credit card”, and “bank” as the two options, but this is just a unit test helper method.  I’m not exposing it anywhere, and even if I did, that’s a lot of extra work (Ok, so it’s a tiny bit of work, but it can feel like a lot #1stWorldProblems).  The simple solution TypeScript offers is string Union Types.
async function selectExistingPayment(type: "CC" | "BANK") {
    getPaymentMethodsSpy.and.callFake(() => ActionsStubs.GetPaymentMethods.fakeResponseWithPaymentCount(2));
    await Payment.setPayments("");
    CommonLib.setValue(Payment.fields.paymentMethod, type === "BANK" ? 1 : 2);
}
So now the calls sites look like this:
image_thumb1
Extremely readable as to what I’m doing, and you get compile time error checking that "Dad" (or any other values besides "CC" and "BANK" ) is not a valid Existing Payment Method!

Friday, November 11, 2016

Learn All the Shortcuts!!!

 

I just installed Mads Kristensen’s Learn the Shortcut extension for Visual Studio.  Within the first 5 minutes, it has already paid for itself (yes it’s free, but it does cost some time to download, install, and restart VS).  

The extension is fairly simple, each time you do something that there exists a shortcut for, it outputs it to a custom output window.  I’ve been wanting to learn the CollapsetoDefinitions shortcut for a while, I just always forget, so I ran that one first, and continued on with my day, hoping that if I do it a few more times, maybe I’ll learn it. 

But then… it showed a shortcut for “RunAllTests”.  I didn’t even think to look for a shortcut for running all tests, even though I navigate to the test explorer and click the “Run All” button multiple times a day.

RunAllShortCut

So not only is the tool great for the shortcuts you know you want to start learning, but just need a little extra help with, it’s also great for those shortcuts that you didn’t even think about possibly having a shortcut for.  Figuring out what you don’t know you don’t know for the win!

LearnAllTheShortCutzzz

Friday, April 24, 2015

How To Ignore Mismatch Processor Architecture Warnings In Unit Test Assemblies With Faked Assemblies

I was attempting to cleanup the warnings in our solution build and ran into this one:
There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "Microsoft.TeamFoundation.WorkItemTracking.Client.DataStoreLoader", "x86". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project. [C:\_Dev\Current Sprint\Xrm.Common.Tests\obj\Debug\Fakes\xrmt\f.csproj]    Xrm.Common.Tests
Researching the error on the web I found that I should be able to use the ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch Project property to ignore the warning.  I added the property to my Unit Test project and it didn't do anything.  After searching for another 20 minutes I ended up re-reading the warning, and noticed that it was referencing a csproj file other than the one that VS was showing as containing the warning. 
A couple more tests and I finally found that you can include MSBuild properties in the Fakes csproj by using the Compilation Element:
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
  <Assembly Name="Xrm.Common"/>
  <Compilation>
    <Property Name="ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch">None</Property>
  </Compilation>
</Fakes>

Problem Solved!

Now on to clearing out the rest of the warnings...