Particular / NServiceBus

Build, version, and monitor better microservices with the most powerful service platform for .NET
https://particular.net/nservicebus/
Other
2.08k stars 647 forks source link

Testing Sagas - "Could not load type 'NServiceBus.IPipelineContextExtensions'" #7163

Open Jamie-Clayton opened 2 days ago

Jamie-Clayton commented 2 days ago

Describe the bug

Description

Trying to automate the testing of Sagas with Specflow BDD and XUnit. This failed quickly.

Expected behavior

I should be able to test my Sagas before using them.

Actual behavior

Exception trying to test a simple SAGA configuration.

Versions

in .net 8 Nservicebus v9.22

Please list the version of the relevant packages or applications in which the bug exists.

<PackageReference Include="FluentAssertions" Version="6.12.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="NServiceBus" Version="9.2.2" />
<PackageReference Include="NServiceBus.Testing" Version="9.0.0" />
<PackageReference Include="SpecFlow.Plus.LivingDocPlugin" Version="3.9.57" />
<PackageReference Include="SpecFlow.xUnit" Version="3.9.74" />
<PackageReference Include="xunit" Version="2.9.2" />

Steps to reproduce

BDD statement Given a Network Meter Identifier (NMI) exists When we run the Participant Roles Policy Then a log will be created

 public class ParticipantRolesPolicyStepDefinitions
 {
     private readonly Mock<ILogger<ParticipantRolesPolicy>> _logger;
     private readonly ScenarioContext _scenario;

     public ParticipantRolesPolicyStepDefinitions(ScenarioContext scenarioContext)
     {
         _logger = new Mock<ILogger<ParticipantRolesPolicy>>();
         _scenario = scenarioContext;
         _scenario.Set(new TestableSaga<ParticipantRolesPolicy, ParticipantRolesData>(() =>
             new ParticipantRolesPolicy(_logger.Object)));
     }

     [Given(@"a Nmi exists")]
     public async Task GivenANmiExists()
     {
         var policy = _scenario.Get<TestableSaga<ParticipantRolesPolicy, ParticipantRolesData>>();
         var link = new Link(new Uri("https://localhost:5001/api/nmi/TestNmi"),"self","GET");
         var startMessage = new MeterServiceRequested("TestNmi", DateOnly.FromDateTime(DateTime.Now.AddDays(28)), Guid.NewGuid(), [link]);
         var result = await policy.Handle(startMessage); 👈 This causes the error: Could not load type 'NServiceBus.IPipelineContextExtensions'

         result.Completed.Should().BeFalse();
         result.FindSentMessage<ConfirmParticipantRoles>().Nmi.Should().Be(startMessage.Nmi);
     }
}

Saga code for reference (nothing complicated)

 public class ParticipantRolesPolicy : Saga<ParticipantRolesData>,
     IAmStartedByMessages<MeterServiceRequested>,
     IHandleMessages<ParticpantRolesConfirmed>
 {
     private readonly ILogger<ParticipantRolesPolicy> _logger;

     ///// <summary>
     ///// Allow this Policy to log information
     ///// </summary>
     ///// <param name="logger"></param>
     public ParticipantRolesPolicy(ILogger<ParticipantRolesPolicy> logger)
     {
         _logger = logger;
     }
    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<ParticipantRolesData> mapper)
 {
     mapper.MapSaga(saga => saga.Nmi)
         .ToMessage<MeterServiceRequested>(message => message.Nmi);
 }

        public async Task Handle(MeterServiceRequested message, IMessageHandlerContext context)
        {
            _logger.LogInformation("Confirm the participant roles for {Nmi}.", message.Nmi);
            var confirmMarketRoles = new ConfirmParticipantRoles(message.Nmi);
            await context.SendLocal(confirmMarketRoles);
            await Task.CompletedTask;
        }

Relevant log output

System.TypeLoadException : Could not load type 'NServiceBus.IPipelineContextExtensions' from assembly 'NServiceBus.Core, Version=9.0.0.0, Culture=neutral, PublicKeyToken=9fc386479f8a226c'.

 ParticipantRolesPolicy.Handle(MeterServiceRequested message, IMessageHandlerContext context)
AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
ParticipantRolesPolicy.Handle(MeterServiceRequested message, IMessageHandlerContext context)
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
BindingInvoker.InvokeBinding(IBinding binding, IContextManager contextManager, Object[] arguments, ITestTracer testTracer, TimeSpan& duration)
TestExecutionEngine.ExecuteStepMatch(BindingMatch match, Object[] arguments, TimeSpan& duration)
TestExecutionEngine.ExecuteStep(IContextManager contextManager, StepInstance stepInstance)
TestExecutionEngine.OnAfterLastStep()
TestRunner.CollectScenarioErrors()

Additional Information

I've tried removing the constructor that injected ILogger<>, but it didn't change the behaviour.

WilliamBZA commented 1 day ago

What is the ScenarioContext? Instead of using _scenario.Set<> and _scenario.Get<>, does the code work if you use the following:

var policy = new TestableSaga<ParticipantRolesPolicy, ParticipantRolesData>(() => new ParticipantRolesPolicy(_logger.Object));
var link = new Link(new Uri("https://localhost:5001/api/nmi/TestNmi"),"self","GET");
var startMessage = new MeterServiceRequested("TestNmi", DateOnly.FromDateTime(DateTime.Now.AddDays(28)), Guid.NewGuid(), [link]);
var result = await policy.Handle(startMessage); 👈 This causes the error: Could not load type 'NServiceBus.IPipelineContextExtensions'

result.Completed.Should().BeFalse();
result.FindSentMessage<ConfirmParticipantRoles>().Nmi.Should().Be(startMessage.Nmi);
Jamie-Clayton commented 1 day ago

Exactly the same error. Debugging and stepping through the logic results in the same logic execution.

// Removing this SpecFlow specific code makes no difference to the step through execution of the codebase.
_scenario.Set(bla)
_scenario.Get<bla>

var policy = new TestableSaga<ParticipantRolesPolicy, ParticipantRolesData>(() => new ParticipantRolesPolicy(_logger.Object));
var link = new Link(new Uri("https://localhost:5001/api/nmi/TestNmi"),"self","GET");
var startMessage = new MeterServiceRequested("TestNmi", DateOnly.FromDateTime(DateTime.Now.AddDays(28)), Guid.NewGuid(), [link]);
var result = await policy.Handle(startMessage); 👈 This ALSO causes the error: Could not load type 'NServiceBus.IPipelineContextExtensions'

Here is a screenshot of the /bin directory that is executing the code. There is no NServicebus DLL in the directory. So I'm thinking its part of the issue. FunctionalTestFailure

Jamie-Clayton commented 1 day ago

Here are the implicit Project references.

NServicebus-SagaTestProject