单元测试的最佳实践
Best practice in Unit Testing
我想知道在单元测试 (C#/NUnit) 中是否有任何最佳实践来测试这样的解决方案:
想象一下,我在 BLL(业务逻辑层)中有方法 GetOrderById(long orderId)
,它通过它的 ID 给我一个订单 :)。所以我需要编写测试,使用硬编码的 Id
参数调用此方法,但是例如 var Id = 1;
如果没有具有此类 ID 的记录,测试将失败
[Test]
public void GetOrderById()
{
//var id = ?
Assert.DoesNotThrow(() => {
_orderService.GetOrderById(id);
});
}
我需要一些解决方案来随时工作,也许创建临时记录?或者更好的解决方案..
有什么想法吗?
So I need to write test that will call this method with hardcoded Id parameter, but for example var Id = 1; If there is no record with such id, test will fail
这表明您不是针对接口编程,或者您正在创建集成测试,而不是单元测试。很有可能,它是两者的结合。
您的 BLL 应该仅通过接口引用数据访问层 (DAL)。它不应该有对 类 的硬编码引用。这些具体 类 应该只在 运行 时间通过注入提供。因此,在测试时,您提供一个模拟的 DAL,当测试提供正确的 Id 时,它将有一个带有特定 Id 的记录。同样,当测试 BLL 正确处理丢失的记录时,它不会有带有该 ID 的记录。
因为 DAL 是模拟的,它完全在测试的控制之下,因此很容易设置这些 success/failure 条件。
首先你必须实现依赖倒置原则,然后你将能够通过依赖注入使用 MockObjects。
你没有提供太多关于你想通过这个测试达到什么目的的信息。我猜您想测试该方法是否能够从数据库中获取订单。正如 David Arno 提到的,这就是集成测试。通常,针对数据库或文件系统的测试很糟糕,并且会导致测试缓慢而脆弱。
如果您已使用接口将数据访问层与业务层分离,则可以使用 Subsititue(在 nUnit 库中)并将其提供给您的业务层。
外汇:
IDataAccess interfaceStub = Substitute.For<IDataAccess>();
如果这还不够,就像我在这种情况下认为的那样,您希望数据访问层 return 对订单服务方法 GetOrderById 有用的东西。您可以创建一个 "testable version" 数据访问层。
可能是这样的:
//A few simple tests
[TestMethod]
public void CheckThatOrderExist()
{
var service = new OrderServiceTestable();
var order = service.GetOrderById(1);//This will be found in the list
Assert.IsNotNull(order);
}
[TestMethod]
public void CheckThatOrderDoesNotExist()
{
var service = new OrderServiceTestable();
var order = service.GetOrderById(2);//This will not be found
Assert.IsNull(order);
}
//Your data access layer
public class OrderService
{
protected virtual IList<Order> OrderList { get; set; }
public Order GetOrderById(int id)
{
return OrderList.SingleOrDefault(x => x.Id == id);
}
}
//Order object
public class Order
{
public int Id { get; set; }
}
//This class inherits the order service and over write the list of orders. An instance of this class is used in the tests.
public class OrderServiceTestable : OrderService
{
protected new List<Order> OrderList;
public OrderServiceTestable()
{
OrderList = new List<Order> {new Order {Id = 1}}; //This will overwrite the list of orders because its virtual in the order service class
}
}
看到我做了什么吗?通过继承真实的 class 并覆盖虚拟的属性或方法。您可以让您的可测试覆盖任何数据,同时您可以测试 OrderService class 中的实际方法。这将有助于进行健壮且快得令人眼花缭乱的测试。您不测试数据访问层,而只测试业务层中的方法。但是,这可能需要您做一些工作。
话虽如此,我还是建议用接口解耦
我想知道在单元测试 (C#/NUnit) 中是否有任何最佳实践来测试这样的解决方案:
想象一下,我在 BLL(业务逻辑层)中有方法 GetOrderById(long orderId)
,它通过它的 ID 给我一个订单 :)。所以我需要编写测试,使用硬编码的 Id
参数调用此方法,但是例如 var Id = 1;
如果没有具有此类 ID 的记录,测试将失败
[Test]
public void GetOrderById()
{
//var id = ?
Assert.DoesNotThrow(() => {
_orderService.GetOrderById(id);
});
}
我需要一些解决方案来随时工作,也许创建临时记录?或者更好的解决方案..
有什么想法吗?
So I need to write test that will call this method with hardcoded Id parameter, but for example var Id = 1; If there is no record with such id, test will fail
这表明您不是针对接口编程,或者您正在创建集成测试,而不是单元测试。很有可能,它是两者的结合。
您的 BLL 应该仅通过接口引用数据访问层 (DAL)。它不应该有对 类 的硬编码引用。这些具体 类 应该只在 运行 时间通过注入提供。因此,在测试时,您提供一个模拟的 DAL,当测试提供正确的 Id 时,它将有一个带有特定 Id 的记录。同样,当测试 BLL 正确处理丢失的记录时,它不会有带有该 ID 的记录。
因为 DAL 是模拟的,它完全在测试的控制之下,因此很容易设置这些 success/failure 条件。
首先你必须实现依赖倒置原则,然后你将能够通过依赖注入使用 MockObjects。
你没有提供太多关于你想通过这个测试达到什么目的的信息。我猜您想测试该方法是否能够从数据库中获取订单。正如 David Arno 提到的,这就是集成测试。通常,针对数据库或文件系统的测试很糟糕,并且会导致测试缓慢而脆弱。 如果您已使用接口将数据访问层与业务层分离,则可以使用 Subsititue(在 nUnit 库中)并将其提供给您的业务层。
外汇:
IDataAccess interfaceStub = Substitute.For<IDataAccess>();
如果这还不够,就像我在这种情况下认为的那样,您希望数据访问层 return 对订单服务方法 GetOrderById 有用的东西。您可以创建一个 "testable version" 数据访问层。
可能是这样的:
//A few simple tests
[TestMethod]
public void CheckThatOrderExist()
{
var service = new OrderServiceTestable();
var order = service.GetOrderById(1);//This will be found in the list
Assert.IsNotNull(order);
}
[TestMethod]
public void CheckThatOrderDoesNotExist()
{
var service = new OrderServiceTestable();
var order = service.GetOrderById(2);//This will not be found
Assert.IsNull(order);
}
//Your data access layer
public class OrderService
{
protected virtual IList<Order> OrderList { get; set; }
public Order GetOrderById(int id)
{
return OrderList.SingleOrDefault(x => x.Id == id);
}
}
//Order object
public class Order
{
public int Id { get; set; }
}
//This class inherits the order service and over write the list of orders. An instance of this class is used in the tests.
public class OrderServiceTestable : OrderService
{
protected new List<Order> OrderList;
public OrderServiceTestable()
{
OrderList = new List<Order> {new Order {Id = 1}}; //This will overwrite the list of orders because its virtual in the order service class
}
}
看到我做了什么吗?通过继承真实的 class 并覆盖虚拟的属性或方法。您可以让您的可测试覆盖任何数据,同时您可以测试 OrderService class 中的实际方法。这将有助于进行健壮且快得令人眼花缭乱的测试。您不测试数据访问层,而只测试业务层中的方法。但是,这可能需要您做一些工作。
话虽如此,我还是建议用接口解耦