1

Does any one know how to write a unit test (using xUnit) for the following Get() Method? Get() method is in the controller and returns list of all Categories:

public class CategoryController : Controller
{
    private MyContext x;

    public CategoryController(MyContext y)
    {
        x = y;
    }

    [HttpGet]
    public ActionResult<IEnumerable<Category>> Get()
    {
        return x.Categories.ToList();
    }
}
Airn5475
  • 2,452
  • 29
  • 51
MZG
  • 327
  • 2
  • 12
  • You should check out some tutrials on xUnit, for example: https://learn.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/test-aspnet-core-services-web-apps – Andy Aug 03 '20 at 19:58

2 Answers2

0

If you are using EFCore as ORM, you can use InMemory database for unit testing. There simple example:

[Fact]
public void TestGet()
{
    _options = new DbContextOptionsBuilder<MyContext>()
        .UseInMemoryDatabase(databaseName: "default")
        .Options;
    var context = new MyContext(_options);
    context.EnsureSeed();
    var controller = new CategoryController(context);

    //Act
    var results = controller.Get();

    //Assert
    Assert.NotNull(results);
    Assert.True(results.Count > 0, "Expected to be greater than 0.");
}

Also you need implement EnsureSeed method. Example:

public static void EnsureSeed(this MyContext dataContext)
{
     //Check if database is created, if not - create
     dataContext.Database.EnsureCreated();

     var category = new Category()
     {
          Id = 1
     };
     dataContext.Categories.Add(category);    
     dataContext.SaveChanges();
}
lobstar
  • 310
  • 4
  • 8
  • Hi, thanks for your answer. Would you please explain what is _options? also Does EnsureSeed() method return list of test data? thanks. – MZG Aug 03 '20 at 20:23
  • @MJZ My bad, _options is object of `DbContextOptions` class. `EnsureSeed` is basicly add data to context, and save changes then. – lobstar Aug 03 '20 at 20:26
  • Thank you! I am new in unit testing and I will try your answer. – MZG Aug 03 '20 at 20:41
0

From what I've seen and read the best way to unit test a controller function is to create an instance of the server host from your test setup and make requests directly to your endpoint - this will allow you test the transport layer of your application like the API contract and Http protocols. The following is an example implemented in .Net Core:

    [Trait]
    public class CategoryControllerTests : IClassFixture<WebApplicationFactory<Startup>>
    {
        // Startup - the entry point of most .net core project
        private readonly WebApplicationFactory<Startup> _factory;

        public CategoryControllerTests(WebApplicationFactory<Startup> factory)
        {
            // Any webhost config needed to run tests against the test
            _factory = factory.WithWebHostBuilder(builder =>
            {
                builder.ConfigureTestServices(services =>
                {
                    // register any mock dependancies there - any dependencies in Startup.cs will hold unless overridden by a mock
                    services.AddScoped(x => new Mock() );
                });
            });

        }

        [Fact]
        public async Task Get_ValidRequest_ReturnsData()
        {
            var client = _factory.CreateClient();
            // Whatever routing protocol you use to define your endpoints
            var url = "/category";
            var response = await client.GetAsync(url);
            response.EnsureSuccessStatusCode();
            var content = await response.Content.ReadAsAsync<List<Category>>();
            
            // Any asserts on your response
            Assert.NotNull(content);
        }
     }

This is dependant on how you have setup the startup/initialisation of your project, it will allow you to test the project as though it were running in a production environment while letting you mock out any dependancies below the transport layer for a true unit test.

Note: the use of IClassFixture<WebApplicationFactory> - this will let you reuse an instance of WebApplicationFactory<Startup> for faster test execution; XUnit will inject this for you as part of the framework.