Home > OS >  How to write Tests in ASP.NET Core app with SQL Server database
How to write Tests in ASP.NET Core app with SQL Server database

Time:01-16

How should unit test look in ASP.NET Core app for controllers? And I have a database SQL Server and my app is connected with my app by EF Core. My views are .cshtml files and in Index method it depends on the data from db. For example it is one of my controllers (HomeController). Also I have services and models in my app and views. I don't know is it a right way to test services or controllers? And how?

public class HomeController : Controller
{
    // ...
    [Route("home")]
    public IActionResult Index()
    {
        var lst = _service.GetAll();
        return View(lst);
    }

    [Route("about")]
    public IActionResult About()
    {
        return View();
    }
}

How to write a test for this? I know that this way is not correct but I don't know another...

[Test]
public async Task CheckHome_SendRequest_ShouldReturnOk() 
{
    // Arrange
    WebApplicationFactory<Startup> webHost = new WebApplicationFactory<Startup>()
               .WithWebHostBuilder(_ => { });

    HttpClient httpClient = webHost.CreateClient();

    // Act
    HttpResponseMessage httpResponse = await httpClient.GetAsync("home");

    // Assert
    Assert.AreEqual(HttpStatusCode.OK, httpResponse.StatusCode);
}

Edit: it can be other type of tests

CodePudding user response:

Alright, so if I'm getting this correctly, the system under test here is the Controller.

So, first thing (that you are already doing) would be to have a decent DI and inject controller's dependencies into the Controller, for example, via contstructor.

Then, you would need to build your DI container and your mocking mechanism such that the Controller instance would be instantiated with mocked dependencies.

Then, you simply ask your DI to give you said instance and make a direct call to its method (or methods) that you are testing.

And then you Assert on either outcome of the controller's method's result, or what methods on the injected mocked dependencies the controller called, or both.

P.S. So, there is no need for HttpClient. By using HttpClient and making an actual web request you test (and also make your test dependable upon) lots of unneccesary stuff, like the network layer of the operation system, pipeline of the asp.net framework, and so on.

CodePudding user response:

One solution is to use an inmemory database using Microsoft.EntityFrameworkCore.InMemory nuget package.

My unit test setup looks like this:

[SetUp]
public void Setup()
{
    var options = new DbContextOptionsBuilder<MyContext>()
        .UseInMemoryDatabase("MyInMemoryTestDb")
        .Options;
    _db = new MyContext(options); //the ef core db context
    _service = new ServiceIWantToTest(_db);
    MockData(); //this adds some testable data to _db using regular EF Core methods
}

[TearDown]
public void TearDown()
{
    _db.Database.EnsureDeleted();
}

CodePudding user response:

you could create a base class for initializing your testserver

public abstract class TestBase : IDisposable
    {
        private bool _databaseInitialized;
        
        private HttpClient? _client;

        private TestServer TestServer { get; }

        public TestBase(Action<IServiceCollection>? testServicesConfiguration = null)
        {
            // create a test server instance
            TestServer = CreateServer(testServicesConfiguration ?? (s => { }));
        }
        
        public static TestServer CreateServer(Action<IServiceCollection> testServicesConfiguration)
        {
            var hostBuilder = new WebHostBuilder()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .ConfigureAppConfiguration(cb =>
                                           {
                                               cb.AddJsonFile("appsettings.json")
                                                   .AddEnvironmentVariables();
                                           })
                .ConfigureTestServices(testServicesConfiguration)
                .UseStartup<Startup>()
                .ConfigureAppConfiguration((_, builder) =>
                                           {
                                               builder.AddUserSecrets<Program>(optional: true);
                                           });

            return new TestServer(hostBuilder);
        }

        public async Task<HttpResponseMessage> GetAsync(string requestUri) =>
            await GetClient().GetAsync(requestUri);

        private HttpClient GetClient()
        {
            // singleton
            return TestServer.CreateClient();
        }
    }    

And create a new inherit class from testBase for the test sets:

public class GetScenarioIT : TestBase
{
    protected virtual string TestEndpoint() => "admin/home";

    [Fact]
    public async Task Get_and_response_ok_status_code()
    {
        // Act
        var response = await GetAsync(TestEndpoint());

        // Assert
        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
    }
}
  • Related