When building a project, ensuring its stability through comprehensive test cases is critical. In this blog, we’ll explore how GitHub Copilot Chat can help automate writing test cases, making the development process smoother and more efficient.

Imagine you’re developing a product management system where CRUD (create, read, update, delete) operations are used for managing product details. Now, let’s see how you would write test cases for its methods using GitHub Copilot.

Test case for adding a product

Let’s start by focusing on one of the core operations: adding a product. We’ll walk through how to write a test case for the AddProduct method in ProductController.cs using GitHub Copilot Chat.

After installing GitHub Copilot Chat, open the Copilot Chat prompt and request it to generate a test case. For example, here is the method for adding a product:


          
public IActionResult AddProduct([FromBody] ProductDetails productDetails)
{
    if (this.ModelState.IsValid)
    {
        this._productService.Add(productDetails);
        return this.Ok();
    }

    return this.BadRequest();
}

To generate a test case for this method using GitHub Copilot, select the method and type a request in the chat prompt. GitHub Copilot will then provide a test case based on your query. You can see the query I used and the response from the copilot in the following images.

Test case for adding a product

       
[Fact]
public void AddProduct_ValidProductDetails_ReturnsOk()
{
    // Arrange
    var options = new DbContextOptionsBuilder<ProductContext>()
        .UseInMemoryDatabase(databaseName: "AddProduct_ValidProductDetails_ReturnsOk")
        .Options;

    using (var context = new ProductContext(options))
    {
        var productService = new ProductService(context);
        var controller = new ProductController(null, productService);
        var productDetails = new ProductDetails
        {
            Id = 1,
            Name = "TestProduct",
            Description = "TestDescription",
            Price = 10.0,
            Stock = 100
        };

        // Act
        var result = controller.AddProduct(productDetails);

        // Assert
        Assert.IsType(result);
        Assert.Equal(1, context.Products.Count());
    }
}

[Fact]
public void AddProduct_InvalidProductDetails_ReturnsBadRequest()
{
    // Arrange
    var options = new DbContextOptionsBuilder<ProductContext>()
        .UseInMemoryDatabase(databaseName: "AddProduct_InvalidProductDetails_ReturnsBadRequest")
        .Options;

    using (var context = new ProductContext(options))
    {
        var productService = new ProductService(context);
        var controller = new ProductController(null, productService);
        controller.ModelState.AddModelError("Name", "Required");
        var productDetails = new ProductDetails
        {
            Id = 1,
            Description = "TestDescription",
            Price = 10.0,
            Stock = 100
        };

        // Act
        var result = controller.AddProduct(productDetails);

        // Assert
        Assert.IsType<BadRequestResult>(result);
        Assert.Equal(0, context.Products.Count());
    }
}

Writing test cases with mocking

Initially, I requested test cases without mocking the service. Now, I’ll ask Copilot to write test cases with mocked services.

Test cases with mocking

       
[Fact]
public void AddProduct_ValidProductDetails_IncreasesCountTwice()
{
    var productServiceMock = new Mock <IProductService>();
    var productList = new List<ProductDetails>();
    productServiceMock.Setup(service => service.Add(It.IsAny<ProductDetails>())).Callback<ProductDetails>(product =>
    {
        productList.Add(product);
        productList.Add(product);
    });

    var controller = new ProductController(null, productServiceMock.Object);
    var productDetails = new ProductDetails
    {
        Id = 1,
        Name = "TestProduct",
        Description = "TestDescription",
        Price = 10.0,
        Stock = 100
    };

    var result = controller.AddProduct(productDetails);

    Assert.IsType(result);
    Assert.Equal(2, productList.Count);
}

[Fact]
public void AddProduct_InvalidProductDetails_DoesNotIncreaseCount()
{
    var productServiceMock = new Mock<IProductService>();
    var productList = new List<ProductDetails>();

    var controller = new ProductController(null, productServiceMock.Object);
    controller.ModelState.AddModelError("Name", "Required");
    var productDetails = new ProductDetails
    {
        Id = 1,
        Description = "TestDescription",
        Price = 10.0,
        Stock = 100
    };

    var result = controller.AddProduct(productDetails);

    Assert.IsType<BadRequestResult>(result);
    Assert.Equal(0, productList.Count);
}

Model validation

Next, I request that Copilot validate all the properties used in the ProductDetails class. Copilot generates test cases for different types of model validation based on my request.

Model validation

       
[Fact]
public void AddProduct_MissingRequiredName_ReturnsBadRequest()
{
    var productServiceMock = new Mock<IProductService>();
    var productList = new List<ProductDetails>();

    var controller = new ProductController(null, productServiceMock.Object);
    controller.ModelState.AddModelError("Name", "Required");
    var productDetails = new ProductDetails
    {
        Id = 1,
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 10,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };

    var result = controller.AddProduct(productDetails);

    Assert.IsType<BadRequestResult>(result);
    Assert.Empty(productList);
}

[Fact]
public void AddProduct_NameExceedsMaxLength_ReturnsBadRequest()
{
    var productServiceMock = new Mock<IProductService>();
    var productList = new List<ProductDetails>();

    var controller = new ProductController(null, productServiceMock.Object);
    controller.ModelState.AddModelError("Name", "Name cannot exceed 50 characters.");
    var productDetails = new ProductDetails
    {
        Id = 1,
        Name = new string('A', 51),
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 10,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };

    var result = controller.AddProduct(productDetails);

    Assert.IsType<BadRequestResult>(result);
    Assert.Empty(productList);
}

[Fact]
public void AddProduct_QuantityOutOfRange_ReturnsBadRequest()
{
    var productServiceMock = new Mock<IProductService>();
    var productList = new List<ProductDetails>();

    var controller = new ProductController(null, productServiceMock.Object);
    controller.ModelState.AddModelError("Quantity", "Quantity must be greater than 0 and less than 100.");
    var productDetails = new ProductDetails
    {
        Id = 1,
        Name = "TestProduct",
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 101,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };

    var result = controller.AddProduct(productDetails);

    Assert.IsType<BadRequestResult>(result);
    Assert.Empty(productList);
}

Model validation.

       
[Fact]
public void AddProduct_InvalidProductDetails_ReturnsBadRequest()
{
    var productServiceMock = new Mock<IProductService>();
    var productList = new List<ProductDetails>();

    var controller = new ProductController(productServiceMock.Object);
    var productDetails = new ProductDetails
    {
        Id = 1,
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 10,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };

    controller.TryValidateModel(productDetails);

    var result = controller.AddProduct(productDetails);

    Assert.IsType<BadRequestResult>(result);
    Assert.Empty(productList);
}

Writing test cases for all methods

To write test cases for all the methods in the file, simply open the file and enter a request in the Copilot Chat prompt window.

Test cases for all methods

       
[Fact]
public void AddProduct_ValidProductDetails_ReturnsOk()
{
    var productServiceMock = new Mock<IProductService>();
    var productList = new List<ProductDetails>();
    productServiceMock.Setup(service => service.Add(It.IsAny<ProductDetails>())).Callback<ProductDetails>(product =>
    {
        productList.Add(product);
    });

    var controller = new ProductController(null, productServiceMock.Object);
    var productDetails = new ProductDetails
    {
        Id = 1,
        Name = "TestProduct",
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 10,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };

    var result = controller.AddProduct(productDetails);

    Assert.IsType<OkResult>(result);
    Assert.Single(productList);
}

[Fact]
public void AddProduct_InvalidProductDetails_ReturnsBadRequest()
{
    var productServiceMock = new Mock<IProductService>();
    var productList = new List<ProductDetails>();

    var controller = new ProductController(null, productServiceMock.Object);
    controller.ModelState.AddModelError("Name", "Required");
    var productDetails = new ProductDetails
    {
        Id = 1,
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 10,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };

    var result = controller.AddProduct(productDetails);

    Assert.IsType<BadRequestResult>(result);
    Assert.Empty(productList);
}

[Fact]
public void GetProduct_ExistingProduct_ReturnsProductDetails()
{
    var productServiceMock = new Mock<IProductService>();
    var productDetails = new ProductDetails
    {
        Id = 1,
        Name = "TestProduct",
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 10,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };
    productServiceMock.Setup(service => service.Get("TestProduct")).Returns(productDetails);

    var controller = new ProductController(null, productServiceMock.Object);

    var result = controller.GetProduct("TestProduct");

    Assert.Equal(productDetails, result);
}

[Fact]
public void GetProduct_NonExistingProduct_ReturnsNull()
{
    var productServiceMock = new Mock<IProductService>();
    productServiceMock.Setup(service => service.Get("NonExistingProduct")).Returns((ProductDetails)null);

    var controller = new ProductController(null, productServiceMock.Object);

    var result = controller.GetProduct("NonExistingProduct");

    Assert.Null(result);
}

[Fact]
public void UpdateProduct_ValidProductDetails_ReturnsOk()
{
    var productServiceMock = new Mock<IProductService>();
    var productDetails = new ProductDetails
    {
        Id = 1,
        Name = "TestProduct",
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 10,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };

    var controller = new ProductController(null, productServiceMock.Object);

    var result = controller.UpdateProduct(productDetails);

    Assert.IsType<OkResult>(result);
}

[Fact]
public void UpdateProduct_InvalidProductDetails_ReturnsBadRequest()
{
    var productServiceMock = new Mock<IProductService>();

    var controller = new ProductController(null, productServiceMock.Object);
    controller.ModelState.AddModelError("Name", "Required");
    var productDetails = new ProductDetails
    {
        Id = 1,
        Description = "TestDescription",
        Price = 10.0m,
        Quantity = 10,
        Category = "TestCategory",
        Manufacturer = "TestManufacturer",
        DateAdded = DateTime.Now
    };

    var result = controller.UpdateProduct(productDetails);

    Assert.IsType<BadRequestResult>(result);
}

[Fact]
public void DeleteProduct_ExistingProduct_ReturnsOk()
{
    var productServiceMock = new Mock<IProductService>();
    productServiceMock.Setup(service => service.Remove("TestProduct"));

    var controller = new ProductController(null, productServiceMock.Object);

    var result = controller.DeleteProduct("TestProduct");

    Assert.IsType<OkResult>(result);
}

[Fact]
public void DeleteProduct_NonExistingProduct_ReturnsOk()
{
    var productServiceMock = new Mock<IProductService>();
    productServiceMock.Setup(service => service.Remove("NonExistingProduct"));

    var controller = new ProductController(null, productServiceMock.Object);

    var result = controller.DeleteProduct("NonExistingProduct");

    Assert.IsType<OkResult>(result);
}

Conclusion

GitHub Copilot Chat simplifies the process of writing NUnit test cases by automating much of the code generation. Whether you need to write tests with or without mocking or perform model validation, Copilot Chat can assist with these tasks, saving time and effort. This makes it a valuable tool for developers looking to increase productivity while ensuring project quality.