1

I'm trying to create an AutoFixture.ISpecimenBuilder for this class:

public class DiscosObjectRelationship<T> where T : DiscosModelBase
{
    private readonly string _linkAddress;
    private readonly IDiscosClient _client;
    private T? _relatedObject;
    
    public async Task<T?> GetRelatedObject()
    {
        return _relatedObject ?? await LazyLoad();
    }
    internal DiscosObjectRelationship(string linkAddress, IDiscosClient client)
    {
        _linkAddress = linkAddress;
        _client = client;
    }

    private async Task<T?> LazyLoad()
    {
        _relatedObject = (await _client.Get<T>(_linkAddress))?.Attributes;
        return _relatedObject;
    }
}

Essentially, all I want it to do is to set the _linkAddress to a random URL from Faker.Net and the _client to a mock from NSubstitute. I've tried to do this like so:

public class DiscosRelationshipSpecimenBuilder: ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        if (request is Type {IsGenericType: true} type && type.GetGenericTypeDefinition() == typeof(DiscosObjectRelationship<>))
        {
            return Activator.CreateInstance(type, Faker.Internet.Url(), Substitute.For<IDiscosClient>()) ?? new NoSpecimen();
        }

        return new NoSpecimen();
    }
}

However, it's throwing an error about not being able to find the constructor for the type. This has me confused as (as far as I can tell) I'm providing the correct arguments (string and IDiscosClient respectively).

The full exception is:

System.MissingMethodException: Constructor on type 'DISCOSweb_Sdk.Models.Relationships.DiscosObjectRelationship`1[[DISCOSweb_Sdk.Models.ResponseModels.Reentries.Reentry, DISCOSweb-Sdk, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' not found.
    at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture)
    at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
    at System.Activator.CreateInstance(Type type, Object[] args)
    at DISCOSweb_Sdk.Tests.Fixtures.AutoFixture.DiscosRelationshipSpecimenBuilder.Create(Object request, ISpecimenContext context) in /home/james/repos/DISOSweb-sdk/src/DISCOSweb-Sdk/DISCOSweb-Sdk.Tests/Fixtures/AutoFixture/DiscosRelationshipSpecimenBuilder.cs:line 17
    at AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
    at AutoFixture.CustomizationNode.Create(Object request, ISpecimenContext context)
    at AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
    at AutoFixture.Kernel.TerminatingWithPathSpecimenBuilder.Create(Object request, ISpecimenContext context)

What am I doing wrong?

Braiam
  • 1
  • 11
  • 47
  • 78
ScottishTapWater
  • 3,656
  • 4
  • 38
  • 81
  • Are you instantiating the type from a plugin? looks like a dependency to the plugin dll was not loaded – Nigel Mar 23 '22 at 18:51
  • Nope, this is all my own code. Although the builder is in my test project and the relationship class is in the main one. I have set it so my test assembly can see internals – ScottishTapWater Mar 23 '22 at 18:53

1 Answers1

4

Activator.CreateInstance() only looks for public constructors by default. If you want to look for other scopes of constructor, you need to use the overload with BindingFlags.

The flags needed are:

const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;

You probably also want to use InvariantCulture, so the overall call would look something like:

return Activator.CreateInstance(type, flags, null, new object[] {myArgs}, CultureInfo.InvariantCulture;)
ScottishTapWater
  • 3,656
  • 4
  • 38
  • 81
Daniel Lorenz
  • 4,178
  • 1
  • 32
  • 39