Integration testing with .NET 6, EF Core 6, and xUnit - integration-testing

Has anyone had any success in setting up integration testing for web applications written in .NET 6 and EntityFramework Core 6, and using SQLite in-memory database? I am having issues with test setup/teardown, so tests which run fine in isolation, start randomly failing when running all tests together.
My test context is set up based on the Microsoft examples:
public class TestApplication : AutofacWebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = FakeJwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = FakeJwtBearerDefaults.AuthenticationScheme;
}).AddFakeJwtBearer();
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(FakeJwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
var connectionString = new SqliteConnectionStringBuilder($"DataSource=file:{Guid.NewGuid()}?cache=shared&mode=memory");
var connection = new SqliteConnection(connectionString.ToString());
connection.Open();
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<MyDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
services.AddDbContext<MyDbContext>(options =>
{
options.UseSqlite(connection);
});
var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<MyDbContext>();
var logger = scopedServices.GetRequiredService<ILogger<TestApplication>>();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
try
{
logger.LogInformation("Initialising in-memory database with test data");
TestData.Initialise(db);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {Message}", ex.Message);
}
});
}
}
which will be used in my test fixtures something like this:
public class WhenGettingPartners
{
[Fact]
public async Task ItShouldAcceptValidRequests()
{
await using var app = new TestApplication();
var client = app.CreateClient().WithRoles(Scopes.PartnerRead);
var result = await client.GetAsync("/Partners");
result.Should().BeSuccessful();
var data = await result.Content.ReadAsType<Partner[]>();
data.Should().NotBeEmpty();
}
[Fact]
public async Task ItShouldRejectUnauthorisedRequests()
{
await using var app = new TestApplication();
var client = app.CreateClient();
var result = await client.GetAsync("/Partners");
result.Should().HaveStatusCode(HttpStatusCode.Unauthorized);
}
}
If I select an individual test case and run it, it works fine; if I select the entire text fixture and run it, it works fine. But when I run the entire test project, then tests will fail randomly, usually when it tries to recreate the database and finds the tables already exist.
I've tried using xUnit's IClassFixture interface to share one instance of TestApplication across all tests in the fixture:
public abstract class ApiTestFixture : IClassFixture<TestApplication>
{
public ApiTestFixture(TestApplication application)
{
App = application;
}
public TestApplication App { get; }
}
public class WhenGettingClients : ApiTestFixture
{
public WhenGettingClients(TestApplication app) : base(app)
{
}
[Fact]
public async Task ItShouldAcceptValidRequests()
{
var client = App.CreateClient().WithRoles(Scopes.ClientRead);
var result = await client.GetAsync("/Clients");
result.Should().BeSuccessful();
var data = await result.Content.ReadAsType<Client[]>();
data.Should().HaveCount(2);
}
[Fact]
public async Task ItShouldRejectUnauthorisedRequests()
{
var client = App.CreateClient();
var result = await client.GetAsync("/Clients");
result.Should().HaveStatusCode(HttpStatusCode.Unauthorized);
}
}
but that fails in exactly the same way.
Update
I am developing using the Rider IDE from JetBrains; the same issue occurs if I run the tests in Visual Studio. However, I noticed that if I test with code coverage in Rider, then the tests all pass! Similarly, if I test with profiling in Rider, then the tests all pass; so I'm wondering if there's something more esoteric going on, that the code coverage runner somehow forces the tests to execute in a manner which is more deterministic or less likely to result in test pollution.

Related

Best way to pass configuration to Topshelf.HostFactory.Run in .Net core

I'm trying to develop/convert current .Net framework code of Task Consumer to Top shelf and .NET core 3.1 . I want to read the config data from json file and use those in start as well as in consume methods.
What is the best and simplest way to pass config data to Task Consumer service and other end points
Any suggestion/comments are helpful.
Thanks in advance .
My current code is
static void Main(string[] args)
{
var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json", true,true).Build();
var test2 = configuration.GetSection("RabbitMQSettings");
Console.WriteLine(HostFactory.Run(cfg => cfg.Service<TaskConsumerService>()));
}
ConsumerService code
public class TaskConsumerService : ServiceControl
{
IBusControl _bus;
IConfiguration _configuration;
public bool Start(HostControl hostControl)
{
_bus = ConfigureBus();
_bus.Start();
return true;
}
public bool Stop(HostControl hostControl)
{
_bus?.Stop(TimeSpan.FromSeconds(30));
return true;
}
IBusControl ConfigureBus()
{
return Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var rabbitMQUrl = ConfigurationManager.AppSettings["RabbitMQSettings:RabbitMQHostUrl"];
cfg.Host(new Uri(rabbitMQUrl) , h =>
{
h.Username(ConfigurationManager.AppSettings["RabbitMQSettings:username"]);
h.Password(ConfigurationManager.AppSettings["RabbitMQSettings:pwd"]);
});
var queue0 = ConfigurationManager.AppSettings["QueName"];
cfg.ReceiveEndpoint(queue0 , e =>
{
e.Consumer<TransformConsumer>();
});
});
}
}
Consumer code
public class TransformConsumer : IConsumer<IExecuteTransform>
{
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
string address = string.Empty;
const string json = "application/json";
public async Task Consume(ConsumeContext<IExecuteTransform> context)
{
m_log.Info(" Transform started.");
// Processing
try
{
address = string.Concat(ConfigurationManager.AppSettings["RabbitMQSettings:RabbitMQHost"] , ConfigurationManager.AppSettings["RabbitMQSettings:queName"]);
IExecuteTransform message = await ConsumeSendEndPoint(context , address);
m_log.Info(" Transform Process completed.");
}
catch ( Exception e )
{
m_log.Error("Transform failed");
throw e;
}
}
}
I'd recommend moving from Topshelf to the .NET Core Generic Host, which is used in the console service sample. It uses the standard .NET Core extension packages for configuration, dependency injection, etc.

dotnet core Subscriber is not being triggered after Publishing

I'm working on a Redis(StackExchange.Redis) PUBSUB system in Dotnet core and everything works normally until after I publish to my channel. The subscriber simply won't ever be triggered.
Here is my code:
program.cs
public static void Main(string[] args)
{
_ = RunConsumer();
BuildWebHost(args).Run();
}
private static async Task RunConsumer()
{
LogWriter logger = new LogWriter();
IEnvironment envProvider = new EnvironmentProvider();
try
{
ICache cache = new Cache(envProvider);
IDataRepository dataRepo = new DatabaseRepository();
PubBusiness publisher = new PubBusiness();
await publisher.ImportData();
}
catch (Exception ex)
{
await logger.WriteLogErrorAsync($"Exception occurred", ex);
Environment.Exit(-1);
}
}
Publisher
// ... code that creates my key
// add my data to a batch and save it to redis
batchTasks.Add(batch.ListRightPushAsync("jobs", key));
batch.Execute();
Task.WaitAll(batchTasks.ToArray());
// publishing
ISubscriber sub = _connection.GetSubscriber();
await sub.PublishAsync("uploaded", key);
Subscriber
var db = _connection.GetDatabase();
ISubscriber sub = _connection.GetSubscriber();
// it will never pass here
await sub.SubscribeAsync("uploaded", async (channel, value) =>
{
var key = (await db.ListLeftPopAsync("jobs")).ToString();
// do my stuff here
});
Messaging disabled by default, follow the link for details
BUT if short, just enter this command to redis cli to enable all:
CONFIG SET notify-keyspace-events KEA
https://redis.io/docs/manual/keyspace-notifications/#configuration

not able to migrated between 2 cosmos containers

I am trying to migrate between data between 2 containers with option StartFromBeginning =true. while migrating I am also making a small modification to the document also. When I add this logic I live sync between collections is not working. I had used Migrating data from old container to new partitioned container using change feed as reference which works. After deploying there seems to be no error but. How can I check what I am doing wrong. I have also enabled application insights.
namespace CosmosContainerMigration.Trigger
{
public class ContainerMigration
{
private IUpdatedDocument updatedDocument;
public ContainerMigration(IUpdatedDocument updatedDocument)
{
this.updatedDocument = updatedDocument;
}
[FunctionName("CosmosContainerMigration")]
public async Task Run([CosmosDBTrigger(
databaseName: "%SourceDatabaseName%",
collectionName: "%ContainerName%",
ConnectionStringSetting = "connectionString",
StartFromBeginning =true,
LeaseCollectionName ="%ContainerLeaseName%",
CreateLeaseCollectionIfNotExists = true)]IReadOnlyList<Document> source,
[CosmosDB(databaseName:"%TargetDatabaseName%",
collectionName:"%ContainerName%",
ConnectionStringSetting = "connectionString")]IAsyncCollector<Document> destination,
ILogger log)
{
log.LogInformation("Documents modified " + source.Count);
foreach (var item in source)
{
try
{
Document updatedItem = await this.updatedDocument.Update(item);
await destination.AddAsync(updatedItem);
}
catch (Exception)
{
log.LogInformation("Failed document ", item.Id);
throw;
}
}
}
}
}

xamarin form + odata

Can any one tell me how to retrive data from OData service using Simple.Odata.Client with in xamarin forms ?
I try by following way :
In Portable Project
public App()
{
GetDocument();
}
public async void GetDocument()
{
String result = await ODataServiceAgent.GetDocuments(skipCount);
}
In OData Service Calls
public static async Task<string> GetDocuments(int skipCount)
{
string json = string.Empty;
try
{
var client1 = new ODataClient(new ODataClientSettings(ServiceConstant.sURL_Base, new System.Net.NetworkCredential(ServiceConstant.NCUserName, ServiceConstant.NCPassword))
{
IgnoreResourceNotFoundException = true,
//OnTrace = (x, y) => Console.WriteLine(string.Format(x, y)),
});
string commands = string.Format(ServiceConstant.sURL_WholeDataByPagging, ServiceConstant.servicePaggingTopCount, skipCount);
IEnumerable<IDictionary<string, object>> configs = client1.FindEntriesAsync(commands).Result;
List<IDictionary<string, object>> configList = ((List<IDictionary<string, object>>)configs.ToList());
json = JsonConvert.SerializeObject(configList);
}
catch (Exception ex)
{
string excepstionMessage = ex.Message;
}
return json;
}
while actual call is happen using "FindEntriesAsync" line its not responding
It in the call to Result. In general it's not a good idea to call Result or Wait on async methods: it may work in some environments (like desktop Windows) but it will deadlock in others, especially mobile ones. So just await it, don't do .Result or .Wait.

How to access HttpContext inside a unit test in ASP.NET 5 / MVC 6

Lets say I am setting a value on the http context in my middleware. For example HttpContext.User.
How can test the http context in my unit test. Here is an example of what I am trying to do
Middleware
public class MyAuthMiddleware
{
private readonly RequestDelegate _next;
public MyAuthMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.User = SetUser();
await next(context);
}
}
Test
[Fact]
public async Task UserShouldBeAuthenticated()
{
var server = TestServer.Create((app) =>
{
app.UseMiddleware<MyAuthMiddleware>();
});
using(server)
{
var response = await server.CreateClient().GetAsync("/");
// After calling the middleware I want to assert that
// the user in the HttpContext was set correctly
// but how can I access the HttpContext here?
}
}
Following are two approaches you could use:
// Directly test the middleware itself without setting up the pipeline
[Fact]
public async Task Approach1()
{
// Arrange
var httpContext = new DefaultHttpContext();
var authMiddleware = new MyAuthMiddleware(next: (innerHttpContext) => Task.FromResult(0));
// Act
await authMiddleware.Invoke(httpContext);
// Assert
// Note that the User property on DefaultHttpContext is never null and so do
// specific checks for the contents of the principal (ex: claims)
Assert.NotNull(httpContext.User);
var claims = httpContext.User.Claims;
//todo: verify the claims
}
[Fact]
public async Task Approach2()
{
// Arrange
var server = TestServer.Create((app) =>
{
app.UseMiddleware<MyAuthMiddleware>();
app.Run(async (httpContext) =>
{
if(httpContext.User != null)
{
await httpContext.Response.WriteAsync("Claims: "
+ string.Join(
",",
httpContext.User.Claims.Select(claim => string.Format("{0}:{1}", claim.Type, claim.Value))));
}
});
});
using (server)
{
// Act
var response = await server.CreateClient().GetAsync("/");
// Assert
var actual = await response.Content.ReadAsStringAsync();
Assert.Equal("Claims: ClaimType1:ClaimType1-value", actual);
}
}
The RC1 version of asp.net 5/MVC6 makes it possible to set HttpContext manually in Unit Tests, which is awesome!
DemoController demoController = new DemoController();
demoController.ActionContext = new ActionContext();
demoController.ActionContext.HttpContext = new DefaultHttpContext();
demoController.HttpContext.Session = new DummySession();
DefaultHttpContext class is provided by the platform.
DummySession can be just simple class that implements ISession class. This simplifies things a lot, because no more mocking is required.
It would be better if you unit test your middleware class in isolation from the rest of your code.
Since HttpContext class is an abstract class, you can use a mocking framework like Moq (adding "Moq": "4.2.1502.911", as a dependency to your project.json file) to verify that the user property was set.
For example you can write the following test that verifies your middleware Invoke function is setting the User property in the httpContext and calling the next middleware:
[Fact]
public void MyAuthMiddleware_SetsUserAndCallsNextDelegate()
{
//Arrange
var httpContextMock = new Mock<HttpContext>()
.SetupAllProperties();
var delegateMock = new Mock<RequestDelegate>();
var sut = new MyAuthMiddleware(delegateMock.Object);
//Act
sut.Invoke(httpContextMock.Object).Wait();
//Assert
httpContextMock.VerifySet(c => c.User = It.IsAny<ClaimsPrincipal>(), Times.Once);
delegateMock.Verify(next => next(httpContextMock.Object), Times.Once);
}
You could then write additional tests for verifying the user has the expected values, since you will be able to get the setted User object with httpContextMock.Object.User:
Assert.NotNull(httpContextMock.Object.User);
//additional validation, like user claims, id, name, roles
take a look at this post:
Setting HttpContext.Current.Session in a unit test
I think what you need is this.
public static HttpContext FakeHttpContext(string url)
{
var uri = new Uri(url);
var httpRequest = new HttpRequest(string.Empty, uri.ToString(),
uri.Query.TrimStart('?'));
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10, true, HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(
httpContext, sessionContainer);
return httpContext;
}
Then you can use it like:
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
HttpContextFactory.Current.Request.Headers.Add(key, value);

Resources