Azure Function: how can I improve the Unit test? - unit-testing

Azure function based on .Net core 3.1. Triggered when a file is uploaded into the Blob container. Any files uploaded to Azure Blob Storage are compressed, and the database is updated.
Below is my business logic
public partial class FileProcessingServiceFacade : IFileProcessingServiceFacade
{
public async Task<string> ProcessAsync(Stream myBlob, string name, ILogger log, ExecutionContext context)
{
AppDbContext.FileRecords.Add(new FileRecords { ... });
AppDbContext.SaveChanges();
return await Task.FromResult($"success");
}
}
public partial class FileProcessingServiceFacade : ServiceBase<FileProcessingServiceFacade>
{
public FileProcessingServiceFacade(AppDbContext appDbContext, IOptions<AppConfigurator> configurator)
: base(appDbContext, configurator) { }
}
I am using xUnit and MOQ for unit testing and below is my unit test
[Fact]
public void ProcessAsync()
{
var filename = "fileName.csv";
var stream = new MemoryStream(File.ReadAllBytes(filename));
var log = Mock.Of<ILogger>();
var executionContext = Mock.Of<ExecutionContext>();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: "Testing")
.Options;
var appDbContext = new AppDbContext(options);
var appSettings = new AppConfigurator(){ ... };
IOptions<AppConfigurator> configurator = Options.Create(appSettings);
var target = new FileProcessingServiceFacade(appDbContext, configurator);
var actual = target.ProcessAsync(stream, filename, log, executionContext);
Assert.True(appDbContext.FileRecords.FirstAsync<FileRecords>().Result.FileName.Equals(filename));
Assert.True(actual.Result.Equals("success"));
stream.Dispose();
}
I am trying to improve the code quality of the unit testing and I would appreciate any suggestions.

The Application test involves one or more external systems, below are the sample points which you need to be aware while have unit testing
Tests may fail if you ae connecting your application with Remote System or External System.
You need to call the external system directly.
Accessing external system may have an effect on the performance of your tests.
Also, As suggested by BrettMiller you can go through the sample of Azure functions Test fixture.

Related

Why won't unit tests connect to a websocket

UPDATE: I've uploaded a repo - https://github.com/mrpmorris/CannotIntegrationTestWebApp/blob/master/TestProject1/UnitTest1.cs
I have a web server that serves both HTTPS and WebSocket requests. When I run the app I am able to connect and make requests from postman for both HTTPS://localhost:8080 and WSS://localhost:8080/game-server
using Gambit.ApplicationLayer;
using Gambit.GameServer.Configuration;
using Gambit.GameServer.UseCases;
namespace Gambit.GameServer;
public class Program
{
public static async Task Main(string[] args)
{
WebApplication app = BuildApp(args);
await RunAppAsync(app);
}
public static WebApplication BuildApp(string[] args, Action<WebApplicationBuilder>? configure = null)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
IServiceCollection services = builder.Services;
IConfiguration configuration = builder.Configuration;
IWebHostEnvironment environment = builder.Environment;
services.AddControllers();
services.AddLogging(opts =>
{
opts.ClearProviders();
opts.AddConfiguration(configuration.GetSection("Logging"));
opts.AddDebug();
opts.AddEventSourceLogger();
#if DEBUG
if (environment.IsDevelopment())
opts.AddConsole();
#endif
});
services.Configure<GameServerOptions>(configuration.GetSection("GameServer"));
services.AddApplicationServices(configuration);
configure?.Invoke(builder);
WebApplication app = builder.Build();
return app;
}
public static async Task RunAppAsync(WebApplication app)
{
app.MapGet("/", () => "Gambit.Server.API is running");
app.AddUserUseCases();
app.AddGameUseCases();
app.MapControllers();
app.UseWebSockets();
await app.RunAsync();
}
}
When I run my unit tests I use the same code to create and run the server (once per test run) my tests are able to make HTTPS requests but not connect via a WebSocket. When I try, I get a 404 error. I experience the same in PostMan.
static IntegrationTestsServer()
{
ConfigureMocks();
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTesting");
var app = Program.BuildApp(Array.Empty<string>(), builder =>
{
builder.WebHost.UseSetting("urls", "https://localhost:8080");
});
Configuration = app.Services.GetRequiredService<IConfiguration>();
GameServerOptions = app.Services.GetRequiredService<IOptions<GameServerOptions>>();
var dbContextOptions = app.Services.GetRequiredService<DbContextOptions<ApplicationDbContext>>();
using var dbContext = new ApplicationDbContext(dbContextOptions);
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
HttpClient = new HttpClient { BaseAddress = new Uri("https://localhost:8080") };
_ = Program.RunAppAsync(app);
}
I can even perform a successful HttpClient.GetAsync("https://localhost:8080") immediately before the ClientWebSocket fails
System.Net.WebSockets.WebSocketException : The server returned status code '404' when status code '101' was expected.
Does anyone have any ideas why this might be?
Set ApplicationName in the WebApplicationOptions sent to WebApplication.CreateBuilder
WebApplication.CreateBuilder
(
new WebApplicationOptions
{
ApplicationName = typeof(Gambit.GameServer.Program).Assembly.GetName().Name // <==
}
);
Now it will be able to find your manifest file when running from a test.
See the following blog post for more of the back story on how I figured it out.
https://thefreezeteam.com/posts/StevenTCramer/2022/08/25/runwebserverintest

Unit test - Mocking Service and Repository/Unit Of Work Layer

In my current implementation I use the Domain Service layer to inject the repositories through Unit Of work.
In some cases, I inject other Services into my Service.
However, I have found difficulties to Mock these objects when making unit tests, because whenever a Service class has injection of dependencies with other Services I need to mock the dependencies of that other service.
How to make it work in a simple way?
Was I using the layers wrongly?
Eg:
public class ValueService : IValueService
{
private readonly ITestService _testService;
private readonly IOptionService _optionService;
public ValueService (IUnitOfWork unitOfWork,
ITestService testService,
IOptionService optionService) : base(unitOfWork)
{
_testService = testService;
_optionService = optionService;
}
When I'm going to mock the ValueService class, I need to mock the TestService and OptionService together with their dependencies.
Can you help me to think about this architecture that I'm implementing?
You can try the code below. I hope it helps.
You can inject all your services and repositories.
public class ValueServiceTest
{
private readonly IValueService _valueService;
public ValueServiceTest()
{
_valueService = GetService();
}
***********************
Your test methods.
***********************
private ValueService GetService()
{
var services = new ServiceCollection();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<IValueService, ValueService>();
services.AddScoped<ITestService, TestService>();
services.AddScoped<IOptionService , OptionService >();
**********************************
You can inject repositories here.
**********************************
var serviceProvider = services.BuildServiceProvider();
return serviceProvider.GetService<IValueService>();
}
}

Is Xamarin Master-Detail Template Broken When used with Azure Backing?

The Master-Detail template in Xamarin has typically been a great starting point for many of my apps that work with .Net Core Backing Service. For review it has a Dependency service in the Client that allows Mocking of the Azure Backing Service or connection to a real or local service while in development.
The control variable generated by the template is public static bool UseMockDataStore = true;
All Code discussed is completely found here: https://github.com/BicycleMark/SignalRGB
I created a Xamarin Master-Detail project naming it SignalRGB and did the following:
The solution project structure looks like:
I start Instance one Visual Studio 2019 And Run SignalRGB.Web
3) Made Note of url: [https://localhost:44300]
4) Opened another Instance of Visual Studio (2) to run client with UseMockDataSource=false / The default it displayed results in Client using MockDataSource
5)went to these lines and updated Client for talking to my local server waiting for an http request :
public static string AzureBackendUrl =
//DeviceInfo.Platform == DevicePlatform.Android ? "http://10.0.2.2:5000" : "http://localhost:44300";
DeviceInfo.Platform == DevicePlatform.Android ? "http://localhost:44300" : "http://localhost:44300";
public static bool UseMockDataStore = false;
public App()
{
InitializeComponent();
if (UseMockDataStore)
{
DependencyService.Register<MockDataStore>();
}
else
{
DependencyService.Register<AzureDataStore>();
}
MainPage = new MainPage();
}
Went and ran SignalRGB.UWP from VS Instance(2) and client application hung on the line GetStringAsync():
bool IsConnected => Connectivity.NetworkAccess == NetworkAccess.Internet;
public async Task<IEnumerable<Item>> GetItemsAsync(bool forceRefresh = false)
{
if (forceRefresh && IsConnected)
{
var json = await client.GetStringAsync ($"api/item");
items = await Task.Run(() => JsonConvert.DeserializeObject<IEnumerable<Item>>(json));
}
return items;
}
I have tried other platforms iOS and get same result:
What Am I doing wrong here?
Oversight? in the templated code perhaps. 44300 is the port but notice the protocol.
DeviceInfo.Platform == DevicePlatform.Android ? "https://localhost:44300" :

.Net Core DynamodDB unit testing with XUnit

Using C#, .net core 2.0, dynamo db
I have my web api, that interact with my dynamo db database having both Get and Post methods.
Example of Mehthod:
[HttpGet("api/data")]
public async Task<List<string>> GetAllData(string userId, string type, string status)
{
var creds = new BasicAWSCredentials(awsId, awsPassword);
var dynamoClient = new AmazonDynamoDBClient(creds, dynamoRegion);
var context = new DynamoDBContext(dynamoClient);
List<ScanCondition> conditions = new List<ScanCondition>();
conditions.Add(new ScanCondition("UserId", ScanOperator.Equal, userId));
conditions.Add(new ScanCondition("Type", ScanOperator.Equal, type));
conditions.Add(new ScanCondition("Status", ScanOperator.Equal, status));
var results = await context.ScanAsync<Common.Job>(conditions, new DynamoDBOperationConfig() { OverrideTableName = MyDynamoTable }).GetRemainingAsync();
return results.Select(x => x.UpdatedBy.ToLower()).ToList();
}
Now I want to write unit/integration tests for my api methods. Earlier I had used NUnit but with .net core 2.0 I believe we have to use XUnit: https://xunit.github.io/docs/getting-started-dotnet-core
Setting up Xunit in my project should not be an issue.
I wanted to know how can I write test which involve dynamo db here. This is the first time I am using any AWS service here.
So bascially I need to know how can I mock up a aws connection, dynamo db and then use various params as shown in my method above.
I could not find much details or any earlier helpful post on this topic so posting one here.
If aws dynamo db part is not testable. Can anyone share the example of xunit test where we can test the params may be and see the expected result?
AWS SDK work with interfaces. You can mock interface IAmazonDynamoDB easily. But try to do it with dependecy injection-ish. Much better.
Something like
private readonly IAmazonDynamoDB dynamodbClient;
private readonly IDynamoDBContext context;
public MyDynamodbHandler(IAmazonDynamoDB client)
{
this.dynamodbClient = client;
this.context = new DynamoDBContext(client);
}
[HttpGet("api/data")]
public async Task<List<string>> GetAllData(string userId, string type, string status)
{
List<ScanCondition> conditions = new List<ScanCondition>();
conditions.Add(new ScanCondition("UserId", ScanOperator.Equal, userId));
conditions.Add(new ScanCondition("Type", ScanOperator.Equal, type));
conditions.Add(new ScanCondition("Status", ScanOperator.Equal, status));
var results = await this.context.ScanAsync<Common.Job>(conditions, new DynamoDBOperationConfig() { OverrideTableName = MyDynamoTable }).GetRemainingAsync();
return results.Select(x => x.UpdatedBy.ToLower()).ToList();
}
So every function uses the injected IAmazonDynamoDB. All you have to do is to mock this instance at the beginning
Such as
dynamodbClientMock = new Mock();
Then use this mock to initiate MyDynamodbHandler class
var dynamodbHandler = new MyDynamodbHandler(dynamodbClientMock);
dynamodbHandler.GetAllData();

Unit Test Web API - How to get auth token

I use token auth for my WebApi application.
I have the following ConfigureAuth method in Startup class:
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
and ApplicationOAuthProvider:
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await userManager.FindAsync(context.UserName, context.Password);
//ApplicationUser user = new ApplicationUser() { UserName ="a" };
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
so, I should call /Token and pass credentials to get token. It works, but I want to create Unit Test for it. Is it possible?
The only way to do that is by make an integration test, which asserts the full pipeline testing - from request to response. Before the actual test on the server, you can call the token endpoint to get it, and then use it in the actual unit test by attaching it to the response. I have a sample, which uses MyTested.WebApi here:
Sample
You can do the same without the testing library, this is just how to do it.
I like the idea of pluggable configuration.
For Unit Test project, I want to use specific identity and get predictable data fro LDAP. So, i use the following line in my unit test method when setting http configuration:
config.Filters.Add(new WebApiSetIdentityFilter(config, identityName));
where the filter just "hacks" the identity, replacing the fields I need:
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
//This principal flows throughout the request.
context.Principal = new GenericPrincipal(new GenericIdentity(this.IdentityName, "LdapAuthentication"), new string[0]);
}