views:

19

answers:

1

I have the following method signature in my iterface:

void SetItem(ref AddressItem item);

I do a parameter constraint like this:

IAddAddressForm form = Substitute.For<IAddAddressForm>();
AddressItem item = null;
form.SetItem(Arg.Is(item));

But that fails because of the ref. If I take the ref out then it works fine. But I need to pass by reference here.

Any ideas how to get this?

Side Note: My ultimate goal is to throw an exception in SetItem if the passed in value is null. If you can help with that then you get extra points!

+1  A: 

NSubstitute does not have direct support for arg matching ref parameters, but in general it will work with them ok.

I'm going to assume you have to use ref as stated in your question, but obviously if you can avoid ref your API will be simpler, as will testing it (irrespective of the frameworks you use).

In answer to your immediate question, you can pass a ref by updating your second code sample to:

form.SetItem(ref item);

For your side note, make sure you are not trying to push too much behaviour into your substitute. I've found that whenever I do this it is a sign I need to simplify the communication between the class I'm testing and its dependencies. (Or if I really need a lot of logic in a fake object, I'll hand code one rather than generate it; it can often be simpler.)

There are a few ways to get this call to throw an exception:

form.When(x => x.SetItem(ref item)).Do(x => { throw new ArgumentNullException(); });

This will throw an exception only when called with a null ref. You can also selectively add this behaviour depending on the argument passed, although I'd advise against this as it is probably a sign you are pushing too much into your substitute.

form.WhenForAnyArgs(x => x.SetItem(ref item))
    .Do(x => {
        if (x[0] == null)
            throw new ArgumentNullException();
    });

Finally, if you just want to check that the class you are testing responds properly when IAddAddressForm throws an arg null exception, I'd probably just do this:

form
    .WhenForAnyArgs(x => x.SetItem(ref item))
    .Do(x => { throw new ArgumentNullException(); });

That way you don't really care what the argument is, you just want to make sure the code you are testing reacts to this case properly.

Hope this helps.

SIDE NOTE:

If you want to use an arg matcher (like Arg.Any<AddressItem>()) for an out or ref argument you'll need to define it outside the call itself (this can be a tad error prone: you'll need to make sure you define the matchers in the same order as they'll go into the call):

        IAddAddressForm form = Substitute.For<IAddAddressForm>();
        AddressItem item = Arg.Is<AddressItem>(y => y.Number == 14);
        form
            .When(x => x.SetItem(ref item))
            .Do(x => { throw new ArgumentNullException(); });
        var address = new AddressItem { Number = 14 };
        form.SetItem(ref address);
David Tchepak
Very complete answer! Thanks a bunch. It may look like I am doing to much in my substitutes, but that is because I am doing a training for my co-workers and my examples are all a bit contrived.
Vaccano