Integrated Unit Testing is not running in ASP.NET core MVC/Web API - unit-testing

I am developing a web API using ASP.Net core. I am doing integrated testing to my project. I am following this link, https://koukia.ca/integration-testing-in-asp-net-core-2-0-51d14ede3968. This is my code.
I have the controller to be tested in the thegoodyard.api project.
namespace thegoodyard.api.Controllers
{
[Produces("application/json")]
[Route("api/category")]
public class CategoryController: Controller
{
[HttpGet("details/{id}")]
public string GetCategory(int id = 0)
{
return "This is the message: " + id.ToString();
}
}
}
I added a new unit test project called thegoodyard.tests to the solution. I added a TestServerFixture class with the following definition
namespace thegoodyard.tests
{
public class TestServerFixture : IDisposable
{
private readonly TestServer _testServer;
public HttpClient Client { get; }
public TestServerFixture()
{
var builder = new WebHostBuilder()
.UseContentRoot(GetContentRootPath())
.UseEnvironment("Development")
.UseStartup<Startup>(); // Uses Start up class from your API Host project to configure the test server
_testServer = new TestServer(builder);
Client = _testServer.CreateClient();
}
private string GetContentRootPath()
{
var testProjectPath = PlatformServices.Default.Application.ApplicationBasePath;
var relativePathToHostProject = #"..\..\..\..\..\..\thegoodyard.api";
return Path.Combine(testProjectPath, relativePathToHostProject);
}
public void Dispose()
{
Client.Dispose();
_testServer.Dispose();
}
}
}
Then again in the test project, I created a new class called, CategoryControllerTests with the following definition.
namespace thegoodyard.tests
{
public class CategoryControllerTests: IClassFixture<TestServerFixture>
{
private readonly TestServerFixture _fixture;
public CategoryControllerTests(TestServerFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task GetCategoryDetai()
{
var response = await _fixture.Client.GetAsync("api/category/details/3");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
bool containMessage = false; //responseString.Contains("This is the message: 3"); - I commented on purpose to make the test fails.
Assert.True(containMessage);
}
}
}
Then I right on the test method and clicked run tests in the option to run the test. But none of the tests was run. This is the output.
What is missing in my code? How can I get my integrated test running?

Please check following NuGet packages in your project:
Microsoft.AspNetCore.TestHost
Microsoft.NET.Test.Sdk
xunit
xunit.runner.visualstudio

Perhaps there's a build error that's preventing the project from being compiled. There's really not enough information here to say for sure. Rebuild your solution, and ensure there's no errors.
Aside from that, you can remove some variables by reducing the test code needed. ASP.NET Core includes a WebApplicationFactory<TEntryPoint> fixture out of the box for bootstrapping a test server. You can therefore change your test code to just:
public class CategoryControllerTests: IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
public CategoryControllerTests(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task GetCategoryDetail()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("api/category/details/3");
...
See the documentation for additional information and more advanced scenarios.

This way works fine for xUnit based intergration tests, which use Startup configuration. the code blow also demonstrates how to override some settings in appSetting.json to specific values for testing, as well as how to access to DI services.
using System;
using System.Net.Http;
using MyNamespace.Web;
using MyNamespace.Services;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace MyNamespace.Tests
{
public class TestServerDependent : IDisposable
{
private readonly TestServerFixture _fixture;
public TestServer TestServer => _fixture.Server;
public HttpClient Client => _fixture.Client;
public TestServerDependent()
{
_fixture = new TestServerFixture();
var myService = GetService<IMyService>();
// myService.PerformAnyPreparationsForTests();
}
protected TService GetService<TService>()
where TService : class
{
return _fixture.GetService<TService>();
}
public void Dispose()
{
_fixture?.Dispose();
}
}
public class TestServerFixture : IDisposable
{
public TestServer Server { get; }
public HttpClient Client { get; }
public TestServerFixture()
{
var hostBuilder = WebHost.CreateDefaultBuilder()
.ConfigureAppConfiguration(
(builderContext, config) =>
{
var env = builderContext.HostingEnvironment;
config
.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile("appsettings.Testing.json", optional: false,
reloadOnChange: true);
})
.ConfigureLogging(
(hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.UseStartup<Startup>();
Server = new TestServer(hostBuilder);
Client = Server.CreateClient();
}
public void Dispose()
{
Server.Dispose();
Client.Dispose();
}
public TService GetService<TService>()
where TService : class
{
return Server?.Host?.Services?.GetService(typeof(TService)) as TService;
}
}
}
How the simple integration test might look like with the described above:
using System.Net;
using Xunit;
namespace MyNamespace.Tests
{
public class SimpleIntegrationTest : TestServerDependent
{
[Fact]
public void RedirectToLoginPage()
{
var httpResponseMessage = Client.GetAsync("/").Result;
// Smoke test to make sure you are redirected (to Login page for instance)
Assert.Equal(HttpStatusCode.Redirect, httpResponseMessage.StatusCode);
}
}
}

Related

asp.net core 6.0 web api unit test using NUnit

I am trying to create the simple web api test for the controller action method I have in my project. I already create and add the test project in my solution. And add the Nunit nuget package in test project.
The controller I am trying to test is look like this:
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IHostEnvironment _hostEnvironment;
private readonly ILogger<HomeController> _logger;
private BaseDataAccess _datatAccess = new BaseDataAccess()
public HomeController(ILogger<HomeController> logger, IConfiguration configuration, IHostEnvironment hostEnvironment)
{
_logger = logger;
_configuration = configuration;
_hostEnvironment = hostEnvironment;
}
[HttpGet("GetInfo/{code}")]
public IActionResult GetInfo(string code)
{
List<InfoModel> infos = new List<InfoModel>();
int isNumber;
if (String.IsNullOrEmpty(code) || !int.TryParse(code, out isNumber))
{
_logger.LogInformation(String.Format("The code pass as arguments to api is : {0}", code));
return BadRequest("Invalid code");
}
try
{
_logger.LogDebug(1, "The code passed is" + code);
SqlConnection connection = _datatAccess.GetConnection(_configuration, _hostEnvironment);
string sql = string.Format ("SELECT * from table1 where code={0}", code);
DataTable dt = _datatAccess.ExecuteQuery(connection,CommandType.Text, sql);
if (dt != null && dt.Rows.Count > 0)
{
foreach (DataRow dr in dt.Rows)
{
infos.Add(new InfoModel
{
ID = dr["id"].ToString(),
code = dr["code"].ToString()
});
}
}
}
catch (Exception ex)
{
_logger.LogError(4, String.Format("Error Message: " + ex.Message + "\n" + ex.StackTrace));
return BadRequest("There is something wrong.Please contact the administration.");
}
return new OkObjectResult(infos);
}
}
Now when I try to create the unit test I need to pass the configuration, hostenvironment and logger to HomeController from my TestHomeController. And I don't know how to instantiate these settings and pass to controller:
using NUnit.Framework;
using Microsoft.AspNetCore.Mvc;
using MyApi.Models;
using MyApi.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MyApi.Tests
{
[TestFixture]
public class TestHomeController: ControllerBase
{
private readonly IConfiguration _configuration; //How to instantiate this so it is not null
private readonly IHostEnvironment _hostEnvironment ;//How to instantiate this so it is not null
private ILogger<HomeController> _logger;//How to instantiate this so it is not null
[Test]
public void GetInfo_ShouldReturnAllInfo()
{
var controller = new HomeConteoller(_logger, _configuration, _hostEnvironment);
var result = controller.GetInfo("11");
var okObjectResult = (OkObjectResult)result;
//Assert
okObjectResult.StatusCode.Equals(200);
}
}
}
Thanks for any help and suggestions.
Probably, you have startup.cs. Don't you?
if you gonna test a controller, then you need to build a whole instance of an application. Here I put an example of how you can test your code if you have Startup.cs.
public class SUTFactory : WebApplicationFactory<Startup>
{
protected override IHostBuilder CreateHostBuilder()
{
return Program.CreateHostBuilder(null);
}
}
public class TestControllerTests
{
private SUTFactory factory;
private HttpClient _client;
public TestControllerTests()
{
factory = new SUTFactory();
_client = factory.CreateClient();
}
[Test]
public async Task GetPatientInterviewID_ShouldReturnAllInterviewID()
{
// Arrange
var id = "11";
// Act
var result = await _client.GetAsync($"Home/GetInfo/{id}");
// Assert
Assert.AreEqual(System.Net.HttpStatusCode.OK, result.StatusCode);
}
}
This example is closer to Integration testing rather than Unit-testing. If you want to have unit-test then you need to do the following things
BaseDataAccess _datatAccess this is a specific realization and it cannot be mocked (comparing to ILogger, IHostEnvironment etc)
move all your code from the controller to a separate class, and test this class.

Unit testing view model that uses SelectMany to call an async method in ReactiveUI

I am new to ReactiveUI and trying to test a view model that looks like this:
public interface IService
{
Task<SessionModel> GetData(string id);
}
/// Provides a group of schedulers available to be used
public interface ISchedulers
{
IScheduler Default { get; }
IScheduler Dispatcher { get; }
}
public class MyVm : ReactiveObject
{
IService service;
public MyVm(ISchedulers schedulers, IService service)
{
this.service = service;
this.session = this.WhenAnyValue(x => x.SessionId)
.SelectMany(SearchSession)
.ObserveOn(schedulers.Default)
.ToProperty(this, x => x.Session);
}
private async Task<SessionModel> SearchSession(string id)
{
return await this.service.GetData(id);
}
private string sessionId;
public string SessionId
{
get => sessionId;
set => this.RaiseAndSetIfChanged(ref sessionId, value);
}
readonly ObservableAsPropertyHelper<SessionModel> session;
public SessionModel Session
{
get { return session.Value; }
}
}
public class SessionModel { }
I'm mocking the service call to return dummy data, but not sure what I need to do with a TestScheduler in order to get the SelectMany to work.
Here's a test class that shows how i would create a test for the view model. The goal is to eventually be able to check that the model got set:
[TestClass]
public class MyVmTests
{
[TestMethod]
public void CreateClass
{
var subject = new MyVm(/*pass in mocks*/);
subject.SessionId="test";
Assert.IsNotNull(subject.Session);
}
}
I don't think using TestScheduler is necessary. The following passes for me (using Moq):
var mockSchedulers = new Mock<ISchedulers>();
mockSchedulers.Setup(s => s.Default).Returns(Scheduler.Immediate);
var id = "123";
var mockService = new Mock<IService>();
var returnSession = new SessionModel();
mockService.Setup(s => s.GetData(It.Is<string>(i => i == id)))
.ReturnsAsync(returnSession);
var target = new MyVm(mockSchedulers.Object, mockService.Object);
target.SessionId = id;
Assert.IsNotNull(target.Session);
Assert.AreEqual(returnSession, target.Session);
TestScheduler is best when you're trying to test something with time (like a Delay, proving that the Delay actually happened). You're not really doing that here.

how to resolve dependency injection in xunit?

I have a problem in creating a test unit using xunit for my services that use dependency injection.
How can i send my dependency to test class?
[Fact]
public async Task GetAllRequestsHavePagingNoFilterNoSortAsync()
{
requestViewModel.RemotePaging = true;
requestViewModel.PageSize = 10;
requestViewModel.Page = 1;
var x = await requestService.GetAsync(requestViewModel);
Assert.NotNull(x);
Assert.True(x.PageCount == requestViewModel.PageSize);
}
You need to mock the dependencies using another library, the most popular one is Moq
So, assuming you have a class like this with a dependency injected:
public class ScrapedJobService : IScrapedJobService
{
private readonly IScrapedJobRepository _scrapedJobRepository;
public ScrapedJobService(IScrapedJobRepository scrapedJobRepository)
{
_scrapedJobRepository = scrapedJobRepository;
}
public WorkingPlace FindWorkingPlace(string title)
{
...
}
}
You need mock that dependency like this:
using Moq;
using Xunit;
private readonly ScrapedJobService _sut;
private readonly Mock<IScrapedJobRepository> _scrapedJobRepo = new Mock<IScrapedJobRepository>();
public ScrapedJobServiceTest()
{
_sut = new ScrapedJobService(_scrapedJobRepo.Object);
}
[Fact]
public void Test()
{
var result = _sut.FindWorkingPlace("Remote");
Assert.Equal(WorkingPlace.Remote, result);
}
Create a startup.cs file then map the dbcontext and the irepositoryDB specification to RepositoryDB concrete class. Dependency inject the IrepositoryDB in the xunit class constructor.
namespace XUnitTestProject1
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.Development.json", false, true)
.Build();
services.AddControllers();
var connectionString = configuration.GetConnectionString("My_DbCoreConnectionString");
services.AddDbContext<MY_DB_Context>(options2 => options2.UseSqlServer(connectionString));
services.AddTransient<IRepositoryDB, RepositoryDB>();
}
}
}
XUnit
public class UnitTest1
{
private readonly IRepositoryDB _repository;
public UnitTest1(IRepositoryDB repository)
{
_repository = repository;
}
public async Task GetItems(long id)
{
IList<myItem> list= await _repository.GetItems(id);
Assert.True(list.Count>0);
}

How do I perform integration test on WebApi controller using FakeItEasy?

I am new at implementing unit tests and integration tests. I am trying to write some integration tests for my application.
Following are the code snippets from my application to give you all the idea of my code.
It would be great help if you could provide me some guidance for it.
namespace MyApplication.ApiControllers
{
[Authorize]
[RoutePrefix("api/customers")]
[AppExceptionFilter]
public class CustomersController : ApiController
{
private readonly IMediator _mediator;
public CustomersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
[Route("GetCustomer")]
public async Task<IHttpActionResult> GetCustomer(string customerNumber, string customerType = null)
{
var result = await _mediator.RequestAsync(new GetCustomerRequest(customerNumber, customerType));
return Ok(result);
}
}
}
Following is the implementation for GetCustomerRequest handler
public async Task<List<Customer>> HandleAsync(GetCustomerRequest request)
{
var result = await customerService.GetCustomer(request.CustomerNumber, request.CustomerType);
// some business logic
return result;
}
Following is the implementation for customerService
public async Task<List<Customer>> GetCustomer(string customerNumber, string customerType = null)
{
using (var dataContext = _dataContextFactory.Invoke())
{
result = await dataContext.Customers
.Where(b => b.CustomerNumber == customerNumber)
.Where(b => b.CustomerType == customerType)
.Select(b => new Customer
{
// Properties assignment...
})
.ToListAsync();
}
return result;
}
Below is the integration unit test what I have tried.
namespace MyApplication.Tests.Integrations
{
[TestFixture]
public class CustomersControllerTests
{
private string _baseAddress;
private string _username;
private string _password;
private IApiClient _apiClient;
[SetUp]
public void Setup()
{
_baseAddress = "https://mywebaaplication.com"; // TODO get this from a config
_username = "";
_password = "";
_apiClient = new ApiClient(new ApiClientAuthenticationHandler(), _baseAddress); // REPLACE with AzureADApiClientAuthenticationHandler
}
[Test]
public async Task CustomersController_GetCustomer()
{
var customerNumber = string.Empty;
var customerType = 500;
var result = await _apiClient.GetAsync<Customer[]>($"/api/customers/GetCustomer?customerNumber={customerNumber}&customerType={customerType}");
Assert.IsNotNull(result);
Assert.IsTrue(result?.Length > 0);
}
}
}
You can do a few things:
Create a webhost within your unit test, then do http requests against it
Not test your controller in a unit test, but in a liveness/readiness check (because it's just glue code anyway). Just do integration testing for your service.
Just test against "new CustomersController"
There isn't a right/wrong answer here. You just look at the risks, and test accordingly. Also depends on the type of code-changes you expect. Sometimes its fine to create the test only within the context of a new change, no need to anticipate everything.

following constructor parameters did not have matching fixture data : DbContextOptions'1options [duplicate]

I have build a WebAPI and apart from my tests running on Postman I would like to implement some Integration/Unit tests.
Now my business logic is very thin, most of the time its more of CRUD actions, therefore I wanted to start with testing my Controllers.
I have a basic setup. Repository pattern (interfaces), Services (business logic) and Controllers.
The flow goes Controller (DI Service) -> Service (DI Repo) -> Repo Action!
So what I did was override my Startup file to change into a in memory database and the rest should be fine (I would assume) Services are added, repos are added and now I am pointing into a in memory DB which is fine for my basic testing.
namespace API.UnitTests
{
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
: base(env)
{
}
public void ConfigureTestServices(IServiceCollection services)
{
base.ConfigureServices(services);
//services.Replace<IService, IMockedService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
base.Configure(app, env, loggerFactory);
}
public override void SetUpDataBase(IServiceCollection services)
{
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
services
.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
}
}
I wrote my first test, but the DatasourceService is not there:
The following constructor parameters did not have matching fixture data: DatasourceService datasourceService
namespace API.UnitTests
{
public class DatasourceControllerTest
{
private readonly DatasourceService _datasourceService;
public DatasourceControllerTest(DatasourceService datasourceService)
{
_datasourceService = datasourceService;
}
[Xunit.Theory,
InlineData(1)]
public void GetAll(int companyFk) {
Assert.NotEmpty(_datasourceService.GetAll(companyFk));
}
}
}
What am I missing?
You can't use dependency injection on test classes. You can only let xunit inject special fixtures via constructor (see docs).
For Integration Testing you want to use the TestServer class from Microsoft.AspNetCore.TestHost package and a separate Startup.cs class (easier to setup configuration than inheritance imho).
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureTestServices(IServiceCollection services)
{
services.Replace(ServiceDescriptor.Scoped<IService, MockedService>());
services.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
public void Configure(IApplicationBuilder app)
{
// your usual registrations there
}
}
In your unit test project, you need to create an instance of the TestServer and perform the test.
public class DatasourceControllerTest
{
private readonly TestServer _server;
private readonly HttpClient _client;
public DatasourceControllerTest()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<TestStartup>());
_client = _server.CreateClient();
}
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.GetAsync($"/api/datasource/{companyFk}");
// expected result from rest service
var expected = #"[{""data"":""value1"", ""data2"":""value2""}]";
// Assert
// This makes sure, you return a success http code back in case of 4xx status codes
// or exceptions (5xx codes) it throws an exception
response.EnsureSuccessStatusCode();
var resultString = await response.Content.ReadAsStringAsync();
Assert.Equals(resultString, expectedString);
}
}
Now, when you call operations which write to the database, you can also check if the data is really written to the database:
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.DeleteAsync($"/api/datasource/{companyFk}");
// expected result from rest service
// Assert
response.EnsureSuccessStatusCode();
// now check if its really gone in the database. For this you need an instance
// of the in memory Sqlite DB. TestServer has a property Host, which is an IWebHost
// and it has a property Services which is the IoC container
var provider = _server.Host.Services;
var dbContext = provider.GetRequiredService<ApplicationDbContext>();
var result = await dbContext.YourTable.Where(entity => entity.Id == companyFk).Any();
// if it was deleted, the query should result in false
Assert.False(result);
}
Now you can use Xunit.DependencyInjection in your tests.
namespace Your.Test.Project
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDependency, DependencyClass>();
}
}
}
your DI-classes:
public interface IDependency
{
int Value { get; }
}
internal class DependencyClass : IDependency
{
public int Value => 1;
}
and XUnit-test:
public class MyAwesomeTests
{
private readonly IDependency _d;
public MyAwesomeTests(IDependency d) => _d = d;
[Fact]
public void AssertThatWeDoStuff()
{
Assert.Equal(1, _d.Value);
}
}