现今大多数的软件工程都是以网路工程为主,那网路工程中又以 Web API 为单位做为开发的基石;因此,今天我们了解如何撰写 Web API 的单元测试,来提升软件开发的准确性。那首先,我们要介绍 Web API (假设部分在阅览的读者没有接触网路工程),Web API —— Web Application Programming Interface,白话文就是网路应用程序与网路应用程序之间沟通的桥梁,那 Web API 可依据使用者开发决定提供 XML、Json、GeoJson、File...等,以下几篇为我觉得不错的 Web API 参考文章,供各位参考:
那通常检测 Web API,都是在检测相对应的 Controller(以浏览器来看是一个对应的 Url),因应不同的 Controller 会有不同的服务如 HttpGet、HttpPost...等,那这次的范例是以 HttpPost 为例。
於是乎,我们就开始撰写 Web API 专案,那这次范例所采用的框架是 N-Tiers 框架,主要流程是
Controller -> IService (实作用 Service) -> Model
而今天的情境是有使用者登录商品的资料,登录完资料後,我们要登记 Log 并且给予这个资料一组 Unique GUID,程序码如下:
Controller 层:
namespace Products.Controllers
{
[Route("api/[controller]")]
public class ProductsController : Controller
{
private readonly ILogger Logger;
private readonly IProductService ProductService;
public ProductsController(ILogger inLogger, IProductService inProductService)
{
Logger = inLogger;
ProductService = inProductService;
}
[HttpPost]
public string Post(Product product)
{
Logger.Log("High", $"Adding a products with an id {product.ProductName}");
var productGuid = ProductService.SaveProduct(product);
return productGuid;
}
}
}
IService 层(服务介面层):
namespace Products.IService
{
// 登记 Log 的服务
public interface ILogger
{
public void Log(string level, string message);
}
// 产品 Product 的服务
public interface IProductService
{
public string SaveProduct(Product product);
}
}
Service 层(服务实作层):
namespace Products.Service
{
// 登记 Log 的服务
public class Logger : ILogger
{
public void Log(string level, string message)
{
// 撰写你要的功能,并非测试重点,所以不详细列述
}
}
// 产品 Product 的服务
public class ProductService : IProductService
{
public string SaveProduct(Product product)
{
// 撰写你要的功能,并非测试重点,所以不详细列述
}
}
}
Model 层(资料模型):
namespace Products.Models
{
public class Product
{
// 产品序号
public string ProductId;
// 产品名称
public string ProductName;
// 产品现货数量
public int QuantityAvailable;
}
}
所以,我们要检测 HttpPost 是否正常运行,可思考几个关注点:
列好了关注点之後,就可以开始撰写测试,如下:
[TestFixture]
public class ProductsControllerTests
{
private ILogger Logger;
private IProductService ProductService;
private ProductsController ProductsController;
[SetUp]
public void SetUp()
{
Logger = Substitute.For<ILogger>();
ProductService = Substitute.For<IProductService>();
ProductsController = new ProductsController(Logger, ProductService);
}
[Test]
public void DemoGuidTest()
{
// Arrange
var guid = "af95003e-b31c-4904-bfe8-c315c1d2b805";
var product = new Product { ProductId = "1", ProductName = "Oven", QuantityAvailable = 3 };
Logger.Log(default, default);
ProductService.SaveProduct(product).Returns(guid);
// Act
var result = ProductsController.Post(product);
// Assert
Assert.AreEqual(result, guid);
}
[Test]
public void DemoSaveProductReceiveTest()
{
// Arrange
var guid = "af95003e-b31c-4904-bfe8-c315c1d2b805";
var product = new Product { ProductId = "1", ProductName = "Oven", QuantityAvailable = 3 };
Logger.Log(default, default);
ProductService.SaveProduct(product).Returns(guid);
// Act
var result = ProductsController.Post(product);
// Assert
ProductService.Received(1).SaveProduct(product);
}
[Test]
public void DemoLogReceiveTest()
{
// Arrange
var guid = "af95003e-b31c-4904-bfe8-c315c1d2b805";
var product = new Product { ProductId = "1", ProductName = "Oven", QuantityAvailable = 3 };
Logger.Log(default, default);
ProductService.SaveProduct(product).Returns(guid);
// Act
var result = ProductsController.Post(product);
// Assert
Logger.Received(1).Log(default, default);
}
}
若实务上,这三个测试都成功,则代表的确有呼叫到 Log 与 SaveProduct 各一次,并且有成功回传 GUID,这样就算是个良好的单元测试组。
文章故事情境参考来源:https://www.michalbialecki.com/2019/01/03/writing-unit-tests-with-nunit-and-nsubstitute/
<<: Day7 Sideproject(作品集) from 0 to 1 - 业务流程
ORM(Object Relational Mapping) 将关联式资料库映射至物件导向的资料抽象...
前言 第一天不免俗的要介绍一下使用的测试环境,其实也不是偷懒,毕竟接下来的文章都会围绕在同一个环境下...
要渲染 Livewire 元件也非常简单,主要会分成两种常用的方法,以下会分别对照 官方文件 来做示...
关於我... hi~我是一个大三的学生,就读医学资讯。因为科系有学到一些资讯,加上系上的必修课需要...
一个会议的结束,依结论要做出的行动,或是待办事项没有人执行。这个会议结束,并没有实质意义。 通常结论...