Nerf
Nerf

Reputation: 938

NSubstitute - Please use specifications for all arguments of the same type. Issue on TeamCity

I have unit test with AutoFixture, NSubstitute and xUnit.

It pass on local dev machine in VS but fails on TeamCity.

Test:

        [Theory, AutoNSubstituteData]
    public async void GetList_StatusError_ShouldReturnBadRequest(
        [Frozen] ICommentsService _commentsService,
        [Frozen] IMerchantsService _merchantsService,
        [Frozen] ICampaignsService _campaignsService)
    {
        // Arrange   

        var output = _fixture.Build<CommentsResult<CommentOutput>>()
            .Without(w => w.Entity)
            .With(x => x.Status, ServiceActionStatus.Error)
            .Create();

        _commentsService.List(Arg.Any<int>(), Arg.Any<string>()).Returns(output);

        var controller = new CommentController(_commentsService, _merchantsService, _campaignsService);
        controller.Request = new HttpRequestMessage();
        controller.Configuration = new HttpConfiguration();

        // Act
        IHttpActionResult actionResult = await controller.GetList(null);
        var contentResult = actionResult as BadRequestErrorMessageResult;

        // Assert
        contentResult.Should().NotBeNull();
        contentResult.Message.Should().NotBeNullOrEmpty();
    }

TeamCity error:

    NSubstitute.Exceptions.AmbiguousArgumentsException: 
    Cannot determine argument specifications to use.
    Please use specifications for all arguments of the same type. at          NSubstitute.Core.Arguments.NonParamsArgumentSpecificationFactory.Create(Object argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications)

CommentsResult look like:

    public class CommentsResult<T> : IServiceResult<T>
    {
    public T Entity { get; set; }
    public string Message { get; set; }
    public Exception Exception { get; set; }
    public ServiceActionStatus Status { get; set; }
     }

What is can be?

Upvotes: 4

Views: 2842

Answers (1)

Alex Povar
Alex Povar

Reputation: 734

Usually this issue happens due to argument specifications which are not entirely consumed (e.g. you pass Arg.Any<T>() to non-virtual method). There are a lot of potential scenarios how that could happen. The reason why you see that on particular environment might be a combination of the corrupted tests and the concurrency (trash from one test is consumed in other test that appeared to run on the same thread). The main problem here is that it might be really hard to troubleshoot the issue if you have a lot of tests, as each one could potentially leak an argument specification.

Recently I've created a diagnostics suite to help to troubleshoot this issue. It allows you to find the "leaked" argument specifications. Notice, it might significantly slow down the tests execution, so should be disabled afterwards.

Steps are following:

  1. Copy the DiagnosticsSubstitutionContext.cs file to your project with tests.
  2. Modify the appropriate place by logging or re-throwing a custom exception with information about all the enqueued specifications. Next time the AmbiguousArgumentsException exception is thrown, this trap should catch a culprit - the specification(s) which is a leftover from the previous test execution. The Spec and CreationStack field values should help you to find an exact place.
  3. Tune your code to trigger the static constructor of the DiagnosticsSubstitutionContext class before tests execution. For instance, you might create a static constructor inside the AutoNSubstituteData attribute class with the following content:

    DiagnosticsSubstitutionContext.Init()
    

After the exception is thrown the next time, you should have information about broken specification, so you will find an exact place.

P.S. If you keep .pdb files during your tests execution, you should be able to see exact line of code where the broken specification was enqueued in addition to the method name.

Upvotes: 3

Related Questions