Search code examples
nsubstituteautofixture

AutoFixture with AutoNSubstituteCustomization: Set object count on ReadOnly IEnumerable<t> property


My test requires that I have different counts of objects in an IEnumerable property of the main entity collection. I have been searching for documentation about this but can't find anything. Here is a sample of what I mean (note that the base entity is created using AutoNSubstituteCustomization)

IFixture fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
var t = fixture.CreateMany<ITransaction>(5)
var service1 = Substitute.For<ITransactionsSvc>();
service1.GetTransactions().ReturnsForAnyArgs(t);
var service2 = Substitute.For<IRequestsSvc>();
service2.GetRequest(default).ReturnsForAnyArgs(
  new Result(){
    TransactionId = t.First().Files.First().RequestId
  }
);

Where ITransaction would look like

public interface ITransaction
{
    long RequestId { get; }
    IEnumerable<FileDef> Files { get; }
    IEnumerable<Comment> Comments { get; }
}

I know I could set fixture.RepeatCount to specify the global count but how can I have a different value for Files and Comments?

I already tried using t.With(x => x.Files, () => fixture.CreateMany<FileDef>(rnd.Next(1,5)) but it throws saying this is a readonly property.

I also tried using NSubstitute .Returns on the t.Files property but for some reason, the type of RequestId got changed from Int64 to Task`1 when trying to read the value for service2 ReturnForAnyArgs response.

I know I had to remove some of the complexity from the real case so that is stays concise so I hope I didn't remove too much and kept it understandable. If you need any precisions, feel welcome to ask.

Sub-question: is there any complete documentation on AutoFixture? On AutoFixture website I was only able to find very introductory documentation.


Solution

  • In fact, as @AndreiIvascu mentionned, the problem AND the cleanest solution I found were linked to NSubstitute. Since NSubstitute is creating the instances, theses instances can be configured using standard NSubstitute calls.

    The solution is simply to use Returns and ReturnsForAnyArgs as I mentionned but it is essential that this newly created substitute is not used directly in the definition of a second substitute as it was the case when accessing the RequestId.

    Note the line var requestId = t.First().Files.First().RequestId; that is now outside of the new Result() definition.

    public void MyTest()
    {
      IFixture fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
    
      var t = fixture.Create<ITransaction>();
      t.Files.Returns(fixture.CreateMany<FileDef>(2).ToList());
      t.Comments.Returns(fixture.CreateMany<Comment>(5).ToList());
    
      var service1 = Substitute.For<ITransactionsSvc>();
      service1.GetTransactions().ReturnsForAnyArgs(t);
    
      var requestId = t.First().Files.First().RequestId;
      var service2 = Substitute.For<IRequestsSvc>();
      service2.GetRequest(default).ReturnsForAnyArgs(
        new Result(){
          TransactionId = requestId
        }
      );
    }