Autofixture test for invalid constructor parameter
Asked Answered
O

2

11

I have the following class and test. I want to test passing a null value as a parameter to the constructor and are expecting an ArgumentNullException. But since I use the Autofixture's CreateAnonymous method I get a TargetInvocationException instead.

What is the correct way to write those kinds of tests?

public sealed class CreateObject : Command {
    // Properties
    public ObjectId[] Ids { get; private set; }
    public ObjectTypeId ObjectType { get; private set; }
    public UserId CreatedBy { get; private set; }

    // Constructor
    public CreateObject(ObjectId[] ids, ObjectTypeId objectType, UserId createdBy) {
      Guard.NotNull(ids, "ids");
      Guard.NotNull(objectType, "objectType");
      Guard.NotNull(createdBy, "createdBy");

      Ids = ids;
      ObjectType = objectType;
      CreatedBy = createdBy;
    }
}

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void constructor_with_null_ids_throw() {
    fixture.Register<ObjectId[]>(() => null);
    fixture.CreateAnonymous<CreateObject>();
}
Osteo answered 25/2, 2013 at 23:16 Comment(1)
I'd do these sort of Guard clause validations at a higher level - See https://mcmap.net/q/1018041/-generalised-func-wrapping-with-anonymous-values-equivalent-to-autofixture-39-with-39. Also, ExpectedExceptionAttribute is a bad idea - if you can't use xUnit at least use some form of Assert.Throws (Yes I know neither of these confront your question regarding TargetInvocationException which is why this isnt an Answer, but step one is to move to clarify the code so you can work with it - even if this is a bug, you'll want to produce a clearer repro and a workaround for the interimWelcher
S
13

IMO, Ruben Bartelink's comment is the best answer.

With AutoFixture.Idioms, you can do this instead:

var fixture = new Fixture();
var assertion = new GuardClauseAssertion(fixture);
assertion.Verify(typeof(CreateObject).GetConstructors());

The Verify method will provide you with a quite detailed exception message if any constructor argument in any constructor is lacking a Guard Clause.


FWIW, AutoFixture extensively uses Reflection, so I don't consider it a bug that it throws a TargetInvocationException. While it could unwrap all TargetInvocationException instances and rethrow their InnerException properties, that would also mean disposing of (potentially) valuable information (such as the AutoFixture stack trace). I've considered this, but don't want to take AutoFixture in that direction, for exactly that reason. A client can always filter out information, but if information is removed prematurely, no client can get it back.

If you prefer the other approach, it's not too hard to write a helper method that unwraps the exception - perhaps something like this:

public Exception Unwrap(this Exception e)
{
    var tie = e as TargetInvocationException;
    if (tie != null)
        return tie.InnerException;
    return e;
}
Sadoff answered 26/2, 2013 at 7:5 Comment(9)
What about checking for ParamName value? If I throw new ArgumentNullException without specifying ParamName or with wrong value, then your test will continue to pass.Beliabelial
If you do that, a static code analysis tool can pick that up and warn you. IIRC, Visual Studio Code Analysis does that.Sadoff
But from TDD perspective it isn't perfect solution :) Of course, I use static analysis tools (R# warn about wrong values), but I want to check ParamName in unit tests. Is it possible in AutoFixture?Beliabelial
IIRC GuardClauseAssertion doesn't do that. Still, there's no reason to throw away a perfectly good tool like Code Analysis just because you're doing TDD: blog.ploeh.dk/2011/04/29/FeedbackmechanismsandtradeoffsSadoff
@VladimirAlmaev Could be worth building your own IdiomaticAssertions (and/or discussing PRing an extension to do arg name comparison)Welcher
@RubenBartelink I have already created a pull request to AutoFixture :)Beliabelial
Hi @MarkSeemann I use AutoFixture heavily on a daily basis, and I find this idiom very useful, however, is there any way to exclude certain parameters from the constructors? For example exclude the optional parameters or any other parameter. Thank youFirstborn
@Firstborn I don't think there is, but you can exclude a particular method or constructor using a LINQ query, and then cover that excluded method or constructor using 'normal' tests.Sadoff
@MarkSeemann oh ok that's what I was doing, thank you for your quick reply. I just want to say how important AutoFixture has become for writing my tests. In one word, it's awesomeFirstborn
C
0

I came across this while I was searching for something similar. I would like to add that, combined with automoqcustomization and xunit, below code also works and its much cleaner.

    [Theory, AutoMoqData]
    public void Constructor_GuardClausesArePresent(GuardClauseAssertion assertion)
    {
        assertion.Verify(typeof(foo).GetConstructors());
    }

You just need to create the AutoMoqData attribute as follows.

    public class AutoMoqDataAttribute : AutoDataAttribute
    {
        public AutoMoqDataAttribute() : base(() => new Fixture().Customize(new AutoMoqCustomization()))
        {

        }
    }
Calamanco answered 6/1, 2018 at 12:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.