Trying to build integration test with connection to db in ServiceStack.
My ServiceStack app is working fine, but when I run simple test I got this error message in line:22
System.MissingMethodException: 'Method not found: 'Int32 ServiceStack.DataAnnotations.CustomFieldAttribute.get_Order()'.'
There is a lite cod:
using ServiceStack;
using ServiceStack.OrmLite;
using ServiceStack.Data;
using NUnit.Framework;
using ServiceStack.DataAnnotations;
using System.Collections.Generic;
namespace oth.Tests.IntegrationTests
{
public class AppHost2 : AppSelfHostBase
{
public AppHost2() : base("Customer REST Example", typeof(CustomerService).Assembly) { }
public override void Configure(Container container)
{
var connectionString = "Host=localhost;Port=5432;Database=test_1234;Username=postgres;Password=local";
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, PostgreSqlDialect.Provider));
using var db = container.Resolve<IDbConnectionFactory>().Open();
db.CreateTableIfNotExists<Customer>();
}
}
public class Customer
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
[Route("/customers", "GET")]
public class GetCustomers : IReturn<GetCustomersResponse> { }
public class GetCustomersResponse
{
public List<Customer> Results { get; set; }
}
public class CustomerService : Service
{
public object Get(GetCustomers request)
{
return new GetCustomersResponse { Results = Db.Select<Customer>() };
}
}
public class CustomerRestExample
{
const string BaseUri = "http://localhost:2000/";
ServiceStackHost appHost;
public CustomerRestExample()
{
//Start your AppHost on TestFixture SetUp
appHost = new AppHost2()
.Init()
.Start(BaseUri);
}
[OneTimeTearDown]
public void OneTimeTearDown() => appHost.Dispose();
/* Write your Integration Tests against the self-host instance */
[Test]
public void Run_Customer_REST_Example()
{
var client = new JsonServiceClient(BaseUri);
var all = client.Get(new GetCustomers());
Assert.That(all.Results.Count, Is.EqualTo(0));
}
}
}
Anytime you see a missing type or missing method exceptions when using the MyGet pre-release packages it means you have a dirty installation (i.e. using pre-release packages from different build times).
In which case you'd need to Clear your Nuget packages cache and download the latest packages again, which ensures all your packages are from the latest same build:
$ dotnet nuget locals all -clear
Related
We are using ServiceStack for our .NET backend and I am trying to work on getting unit testing into the project. However there are some automated tools within ServiceStack that makes it a bit complicated to isolate the units so I could really use some advice. In the example below I would like to unit test a simple service that basically does the following:
Takes a request DTO
Passes the DTO to the repository
Gets back a domain model
If the model exists, it maps it to a responseDTO using Automapper and returns it as a part of an IHTTPResult
So the problem I have is that it seems like Automapper is automatically added to the ServiceStack application and in the application the mapper are registered by just calling:
AutoMapping.RegisterConverter().
So how could I inject this into the service to be able to do the unittest?
Example test:
using AutoMapper;
using FluentAssertions;
using NSubstitute;
namespace Api.Services.Tests.Unit;
public class OrderApiServiceTests
{
private readonly OrderApiService _sut;
private readonly IOrderApiRepository accountApiRepository = Substitute.For<IOrderApiRepository>();
public OrderApiServiceTests()
{
_sut = new OrderApiRepository(orderApiRepository);
var config = new MapperConfiguration(cfg => ApiDtoMapping.Register());
var mapper = config.CreateMapper();
}
[Fact]
public async Task Get_ShouldReturnAccount_WhenAccountExistsAsync()
{
// Arrange
var order = new Order
{
Name = "MyOrder",
Value = 1000,
};
var expectedResponse = new OrderApiDto
{
Name = "MyOrder",
Value = 1000,
};
orderApiRepository.GetAsync(Arg.Any<GetOrder>()).Returns(order);
// Act
var result = await _sut.Get(new GetOrder());
// Assert
result.StatusCode.Should().Be(System.Net.HttpStatusCode.OK);
result.Response.Should().BeEquivalentTo(expectedResponse);
}
}
Added a full example including all files:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
app.UseHttpsRedirection();
}
app.UseServiceStack(new AppHost());
app.Run();
// Configure.AppHost.cs
using Funq;
using ssUnitTests.ServiceInterface;
[assembly: HostingStartup(typeof(ssUnitTests.AppHost))]
namespace ssUnitTests;
public class AppHost : AppHostBase, IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services =>
{
});
public AppHost() : base("ssUnitTests", typeof(MyServices).Assembly) { }
public override void Configure(Container container)
{
container.RegisterAutoWiredAs<OrderRepository, IOrderRepository>().ReusedWithin(ReuseScope.None);
// Configure ServiceStack only IOC, Config & Plugins
SetConfig(new HostConfig
{
UseSameSiteCookies = true,
});
Mappings.RegisterConverters();
}
}
// Mappings.cs
using ssUnitTests.ServiceModel;
namespace ssUnitTests;
public static class Mappings
{
public static void RegisterConverters()
{
AutoMapping.RegisterConverter((Order from) =>
{
var to = from.ConvertTo<OrderDto>();
to.DtoProperty = from.BaseProperty + "Dto";
return to;
});
}
}
// IOrderRepository.cs
using ssUnitTests.ServiceModel;
namespace ssUnitTests.ServiceInterface;
public interface IOrderRepository
{
Order GetOrder();
}
// Order.cs
namespace ssUnitTests.ServiceModel;
public class Order
{
public string Name { get; set; }
public string BaseProperty { get; set; }
}
// OrderDto.cs
namespace ssUnitTests.ServiceModel;
public class OrderDto
{
public string Name { get; set; }
public string DtoProperty { get; set; }
}
// OrderRequest.cs
using ServiceStack;
namespace ssUnitTests.ServiceModel;
[Route("/order")]
public class OrderRequest : IReturn<OrderDto>
{
public int Id { get; set; }
}
// UnitTest.cs
using NSubstitute;
using NUnit.Framework;
using ssUnitTests.ServiceInterface;
using ssUnitTests.ServiceModel;
namespace ssUnitTests.Tests;
public class UnitTest
{
private readonly MyServices _sut;
private readonly IOrderRepository _repository = Substitute.For<IOrderRepository>();
public UnitTest()
{
_sut = new MyServices(_repository);
}
[Test]
public void Get_ShouldReturn_OrderDto()
{
var order = new Order
{
Name = "MyName",
BaseProperty = "MyBaseProperty"
};
_repository.GetOrder().Returns(order);
var response = (OrderDto)_sut.Any(new OrderRequest { Id = 1 });
Assert.That(response.Name.Equals(order.Name));
Assert.That(response.DtoProperty.Equals(order.BaseProperty + "Dto"));
}
}
ServiceStack.dll does not have any dependencies to any 3rd Party Libraries, e.g. it's built-in AutoMapping is a completely different stand-alone implementation to AutoMapper.
If you're using AutoMapper you can ignore ServiceStack's AutoMapping which is completely unrelated.
I'm getting exception on constructing DateOnly variables/fields with AutoFixture.
(constructing of TimeOnly works fine)
AutoFixture.ObjectCreationExceptionWithPath : AutoFixture was unable to create an instance from System.DateOnly because creation unexpectedly failed with exception. Please refer to the inner exception to investigate the root cause of the failure.
AutoFixture, AutoFixture.NUnit3 nugets version: 4.17.0
using AutoFixture;
using AutoFixture.NUnit3;
using NUnit.Framework;
namespace UnitTests
{
[TestFixture]
public class AutoFixtureCreateTests
{
private readonly Fixture fixture = new();
[SetUp]
public void Setup()
{
var date = fixture.Create<DateOnly>(); //fails
var time = fixture.Create<TimeOnly>(); //works fine
}
[Test, AutoData]
public void CreateString(string str) { } //works fine
[Test, AutoData]
public void CreateDateOnly(DateOnly date) { } //fails
[Test, AutoData]
public void CreateTimeOnly(TimeOnly time) { } //works fine
}
}
The answer: at the moment does not.
There is a pull request: https://github.com/AutoFixture/AutoFixture/pull/1305
(however it's in open state almost for a year, without any milestones)
But there is a workaround.
My temporary solution is to create AutoFixture customization (CustomFixture.cs) file and include it to the project:
using AutoFixture;
using AutoFixture.NUnit3;
namespace UnitTests
{
public class CustomFixture
{
public static Fixture Create()
{
var fixture = new Fixture();
fixture.Customize<DateOnly>(composer => composer.FromFactory<DateTime>(DateOnly.FromDateTime));
return fixture;
}
}
public class CustomAutoDataAttribute : AutoDataAttribute
{
public CustomAutoDataAttribute()
: base(()=>CustomFixture.Create())
{}
}
}
After it include customization in the test code:
using AutoFixture;
using AutoFixture.NUnit3;
using NUnit.Framework;
namespace UnitTests
{
[TestFixture]
public class AutoFixtureCreateTests
{
private readonly Fixture fixture =
CustomFixture.Create(); //custom factory
[SetUp]
public void Setup()
{
var date = fixture.Create<DateOnly>(); //now works
var time = fixture.Create<TimeOnly>();
}
[Test, AutoData]
public void CreateString(string str) { }
[Test, CustomAutoData] //custom attribute
public void CreateDateOnly(DateOnly date) { } //now works
[Test, AutoData]
public void CreateTimeOnly(TimeOnly time) { }
}
}
packages:
.net core 3.1,
Specflow 3.8.7
Solution Structure:
I have Step definitions in project UMW.Selenium.UI (A)
namespace UMW.Selenium.UI.Steps
{
[Binding]
public class CalculatorStepDefinitions : UIFramework
{
UIBrowser uiBrowser;
public CalculatorStepDefinitions()
{
uiBrowser = new UIBrowser();
}
[Given(#"the first number is (.*)")]
public void GivenTheFirstNumberIs(int p0)
{
uiBrowser.NavigateToURL("https://demoqa.com/browser-windows");
}
}
}
I have Hooks (BeforeTestRun, BeforeScenario etc.) in another project Selenium.UI.Framework (B).
namespace Selenium.UI.Framework.Framework.Utilities.ScenarioFactory
{
using LogBuffer = List<string>;
[Binding]
[TestClass]
public class SetupAndTearDown
{
internal readonly ScenarioContext _scenarioContext;
internal readonly FeatureContext _featureContext;
private readonly IObjectContainer _objectContainer;
public SetupAndTearDown()
{
}
public SetupAndTearDown(IObjectContainer objectContainer, FeatureContext featureContext, ScenarioContext scenarioContext)
{
this._objectContainer = objectContainer;
_featureContext = featureContext;
_scenarioContext = scenarioContext;
}
[BeforeTestRun]
public static void InitializeTestSuite()
{
ReportsFactory.Report.StartTestSuite();
}
[BeforeScenario]
public void InitializeTestScenario()
{
ReportsFactory.Report.StartTestCase();
//_objectContainer.RegisterInstanceAs(Webdriver.Driver);
}
}
}
When I execute scenario from A, it does not call BeforeTestRun/BeforeScenario from B. Here project A uses functions from project B. The test runs successfully bypassing hooks.
You need to declare bindings from an external assembly in specflow.json.
{
"stepAssemblies": [
{
"assembly": "Selenium.UI.Framework"
}
]
}
Note: the name of the assembly, not the namespace, is required with no file extension. You will need to double check the name of the DLL file created by the Selenium.UI.Framework project.
How to bind classes with required connection string in constructor using Ninject?
Here are the classes that I am using:
AppService class:
using SomeProject.LayerB;
namespace SomeProject.LayerA;
{
public class AppService
{
private readonly ISomeRepository someRepository;
public LocationManagementService(ISomeRepository someRepository)
{
this.someRepository = someRepository;
}
// other codes ...
}
}
SomeRepository class:
namespace SomeProject.LayerB;
{
public class SomeRepository : ISomeRepository
{
private readonly SomeDbContext context;
public SomeRepository(SomeDbContext context)
{
this.context = context;
}
// other codes ...
}
}
SomeDbContext class:
namespace SomeProject.LayerB;
{
public class SomeDbContext : DbContext
{
public SomeDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
// other codes ...
}
}
Then, I use a Ninject module containing the following code:
namespace SomeProject.LayerC;
{
public class SomeModule : NinjectModule
{
public override void Load()
{
Bind<ISomeRepository>().To<SomeRepository>();
// My problem is on this part.. I want to provide the connection string on the
// main program, not here on this class.
// Bind<SomeDbContext>().ToSelf().WithConstructorArgument("nameOrConnectionString", "the connection string I want to inject");
}
}
}
Main program:
using SomeProject.LayerA;
using SomeProject.LayerC;
namespace SomeProject.LayerD;
{
public class MainProgram
{
public MainProgram()
{
IKernel kernel = new StandardKernel(new SomeModule());
AppService appService = kernel.Get<AppService>();
}
}
}
NOTE: The only layer that main program can reference is LayerA where AppService class is located and as well as LayerC where the ninject module is found.
Add a Configuration class like this:
public class Config
{
public static string ConnectionString { get; set; }
}
and in your ninject module write this:
Bind<SomeDbContext>().ToSelf()
.WithConstructorArgument("nameOrConnectionString",
c => Config.ConnectionString);
then in your main method you could write following:
public class MainProgram
{
public MainProgram()
{
IKernel kernel = new StandardKernel(new SomeModule());
Config.ConnectionString = "The connection string";
AppService appService = kernel.Get<AppService>();
}
}
Update:
You can use ninject to locate config class also if you don't want use static methods:
class Config2
{
public string ConnectionString { get; set; }
}
in module:
Bind<Config2>().ToSelf().InSingletonScope();
Bind<SomeDbContext>().ToSelf()
.WithConstructorArgument("nameOrConnectionString",
c=>c.Kernel.Get<Config2>().ConnectionString);
in main:
IKernel kernel = new StandardKernel(new SomeModule());
var conf = kernel.Get<Config2>();
conf.ConnectionString = "The connection string";
AppService appService = kernel.Get<AppService>();
I have a command handler that invokes an operation on a domain object which in turn fires an event when the operation has been executed. I'd like to test that an event handler receives the event when the corresponding command has been sent (see below, some code omitted for brevity). The event handler (MyEventConsumer.Consume) is never invoked even though the event message is published on the bus (loopback bus in this case). Any ideas?
//Test
[TestFixture]
public class TestSendCommandReceiveEvent
{
[Given]
public void installation_of_infrastructure_objects()
{
container.Register(Component.For<MyEventConsumer>().UsingFactoryMethod(() => new MyEventConsumer(_received)));
container.Register(
Component.For<IServiceBus>()
.UsingFactoryMethod(() => ServiceBusFactory.New(x => { x.ReceiveFrom("loopback://localhost/mt_client"); x.Subscribe(conf => conf.LoadFrom(container)); })));
}
[When]
public void sending_a_command()
{
var LocalBus = container.Resolve<IServiceBus>();
LocalBus.Publish(new DoSomething(_aggregateId));
}
[Then]
public void corresponding_event_should_be_received_by_consumer()
{
_received.WaitOne(5000).ShouldBeTrue();
}
}
public class MyEventConsumer : Consumes<SomethingDone>.All
{
private readonly ManualResetEvent _received;
public MyEventConsumer(ManualResetEvent received)
{
_received = received;
}
public void Consume(SomethingDone message)
{
_received.Set();
}
}
//Command handler
public class DoSomethingCommandHandler : Consumes<DoSomething>.All where T:class
{
public void Consume(DoSomething message)
{
var ar = Repository.GetById<SomeAR>(message.ArId);
ar.DoSomething();
Repository.Save(ar, Guid.NewGuid(), null);
}
}
//Domain object
public class SomeDomainObject : AggregateBase
{
public void DoSomething()
{
RaiseEvent(new SomethingDone(Id, 1));
}
}
This passes for me:
// Copyright 2012 Henrik Feldt
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
using System;
using System.Threading;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Magnum.Extensions;
using Magnum.TestFramework;
using MassTransit;
using NUnit.Framework;
namespace ConsoleApplication11
{
[TestFixture]
public class TestSendCommandReceiveEvent
{
ManualResetEventSlim _received = new ManualResetEventSlim(false);
IWindsorContainer _container;
[Given]
public void installation_of_infrastructure_objects()
{
_container = new WindsorContainer();
_container.Register(
Component.For<IServiceBus>()
.UsingFactoryMethod(() => ServiceBusFactory.New(x =>
{
x.ReceiveFrom("loopback://localhost/mt_client");
x.Subscribe(conf =>
{
conf.Consumer(() => new MyEventConsumer(_received));
conf.Consumer(() => new MyCmdConsumer());
});
})));
when();
}
public void when()
{
var localBus = _container.Resolve<IServiceBus>();
// wait for startup
localBus.Endpoint.InboundTransport.Receive(c1 => c2 => { }, 1.Milliseconds());
localBus.Publish(new DoSomething());
}
[Then]
public void corresponding_event_should_be_received_by_consumer()
{
_received.Wait(5000).ShouldBeTrue();
}
}
[Serializable]
public class DoSomething
{
}
[Serializable]
public class SomethingDone
{
}
public class MyEventConsumer : Consumes<SomethingDone>.All
{
readonly ManualResetEventSlim _received;
public MyEventConsumer(ManualResetEventSlim received)
{
_received = received;
}
public void Consume(SomethingDone message)
{
_received.Set();
}
}
public class MyCmdConsumer : Consumes<DoSomething>.Context
{
public void Consume(IConsumeContext<DoSomething> ctx)
{
Console.WriteLine("consumed cmd");
ctx.Bus.Publish(new SomethingDone());
}
}
}
In my experience, there is a short period of time, right after creation of the bus instance, during which any published messages are lost. Must be some kind of async initialization going on.
Try adding a delay between container.Resolve<IServiceBus>() and LocalBus.Publish(new DoSomething(_aggregateId)).
Thread.Sleep did not work in my case, but a Console.ReadLine() surprisingly did!