中间地带存在吗?(单元测试与集成测试)

本文关键字:集成测试 单元测试 存在 中间地带 | 更新日期: 2023-09-27 17:58:23

考虑Repository模式(或类似模式)的实现。我会尽量保持例子/插图的简洁性:

interface IRepository<T>
{
    void Add(T entity);
}
public class Repository<T> : IRepository<T>
{
    public void Add(T entity)
    {
        // Some logic to add the entity to the repository here.
    }
}

在该特定实现中存储库由接口IRepository定义为具有将实体添加到存储库的一个方法,从而使Repository依赖于泛型类型T(此外,Repository必须隐式地依赖于另一种类型TDataAccessLayer,因为抽象是Repository模式的全部点。然而,这种依赖目前还不可用)。在这一点上,根据我目前的理解,我有两个选择:单元测试和集成测试。

在集成测试可能被假设有更多的移动部件的情况下,我更愿意最初进行单元测试,以便至少验证基线功能。然而,如果不创建某种"实体"属性(泛型类型T),我看不出有任何方法可以断言任何逻辑实际上是在Repository实现的Add()方法中执行的。

单元测试和集成测试之间是否存在中间地带,允许(通过反射或其他方式)验证在测试单元内是否达到了特定的执行点?

我对这个特定问题的唯一解释是从存储库中进一步抽象数据访问层,导致Add()方法不仅接受实体参数,还接受数据访问参数。然而,在我看来,这可能会违背Repository模式的目的,因为Repository的使用者现在必须了解数据访问层。

关于请求示例:

(1) 关于单元测试,根据我对当前测试技术的理解,我不确定像Repository这样的东西是否真的可以进行单元测试。因为存储库是围绕特定数据访问层的抽象(包装器),所以似乎唯一的验证方法是集成测试?(诚然,存储库接口可能不与任何特定的DAL绑定,但任何已实现的存储库都必须与特定的DAL实现绑定,因此需要能够测试Add()方法是否确实执行了一些工作)。

(2) 就集成测试而言,根据我对该技术的理解,该测试将通过实际调用Add()方法(该方法应向存储库中添加一条记录)来验证Add()法的执行情况,然后检查数据是否实际添加到存储库中(或者在特定场景中可能是数据库中)。这可能看起来像:

[TestMethod]
public void Add()
{
    Repository<Int32> repository = new Repository<Int32>();
    Int32 testData = 10;
    repository.Add(testData);
    // Intended to illustrate the point succinctly. Perhaps the repository Get() method would not
    // be called (and a DBCommand unrelated to the repository issued instead). However, assuming the
    // Get() method to have been previously verified, this could work.
    Assert.IsTrue(testData == repository.Get(testData));
}

因此,在本例中,假设存储库是某个数据库逻辑层的包装器,那么在测试期间数据库实际上会被命中两次(一次在插入期间,一次在检索期间)。

现在,我认为有用的是一种在运行时验证是否采用了特定执行路径的技术。例如,如果传入非null引用,则采用验证执行路径a,如果传入null引用,那么采用验证执行道路B。此外,也许可以验证要执行特定的LINQ查询。因此,数据库在测试过程中从未真正命中(允许在没有任何实际DAL的情况下对实现进行原型设计和开发)。

中间地带存在吗?(单元测试与集成测试)

这听起来像是在描述实现细节的测试,而不是由模式的实现者来满足模式的要求。在测试单元中是否达到了"特定的执行点"并不重要,重要的是具体的实现者是否支持接口的契约。测试完全可以为测试目的创建T实体,这就是mock的用途。

如果您想进行集成测试,您需要使用真实的数据库。但是,如果您想快速测试,可以尝试内存中的数据库。问题是你能测试什么,不能测试什么。只要你的数据库访问代码是特定于数据库的,你就使用了一个外部系统(停留在单元测试中),你应该模拟它。但是,由于你真的想知道你的数据是否最终在数据库中,你需要对照真实的数据库进行测试。

但是,如果您使用一些数据库抽象,例如ORM映射器,您可以使用ORM映射程序并测试映射是否正确工作。然后,ORM映射器可以使用内存中的数据库进行测试,以检查ORM映射程序是否按预期工作。

如果你不使用ORM映射器,而你创建了一个额外的数据库抽象层,只是为了有一个抽象,所以你的代码只是为了有你想在真正的单元测试中发现的错误而执行的,这不会让你更有效率。

不要进行单元测试。只做集成测试。实现单元测试非常容易,而且毫无用处,尤其是在存储库模式中。几乎所有的错误都发生在直接处理数据库和数据逻辑时。单元测试找不到这样的错误。除非你用一种有很多可能结果的复杂计算方法,否则完全避免单元测试。另一个好处是使用相同的代码进行负载测试。您询问的中间/临时解决方案将使您一无所获。

用户故事是什么

从DB 获取员工详细信息

如何完成任务

编码并测试是否从数据库中提取了员工详细信息。写下你能想到的所有情景。我们只是在情景中交谈(没有什么比这更糟的了)。

集成测试或功能测试必须直接处理数据库/源代码。没有模仿。这些就是步骤。您想要测试getEmployee(emp_id)。以下这5个步骤都是在一个单独的测试方法中完成的。

  1. 删除数据库
  2. 创建数据库并填充角色和其他基础数据
  3. 创建ID为的员工记录(不应使用存储库或工作代码)。您需要另一个将数据放入的代码测试项目中的DB。设定种子需要一个代码集
  4. 使用此ID并调用repository.getEmployee(emp_ID)//这是您编写的需要测试的代码

现在断言()/验证返回的数据是否正确

这证明了getEmployee()是有效的。步骤4要求您的代码只能由测试项目使用。步骤4调用应用程序代码。我的意思是创建一个雇员(步骤3)应该通过测试项目代码而不是应用程序代码来完成。如果有创建雇员的应用程序代码(例如:CreateEmployee()或Repository.Add(Employees)),则不应使用此代码。同样,当我们测试CreateEmployee()时,不应该使用GetEmployee()应用程序代码。我们应该有一个用于从表中获取数据的测试项目代码。

这样就没有嘲笑了!删除并创建数据库的原因是为了防止数据库中存在损坏的数据。使用我们的方法,无论我们运行多少次,测试都会通过

你谈到了场景,这个代码被执行了吗,或者如果条件被执行了,等等。这需要用输入数据和输出数据(来自原始源,无论是DB还是日志文件)进行验证。

例如,如果传入非null引用,请验证采用执行路径A并且如果传入空引用,则采用验证执行路径B。

所以您传入了对存储库方法的null引用。然后调用DB(使用测试项目代码)并检查错误表是否有条目。或者上传日志文件,检查是否添加了错误(再次使用您自己的测试代码或第三方组件)。您可能认为自己在测试项目中做了大量的自定义代码。事实上,这些都是一些简单的即放即用,你只需做一次,就可以永远留在这里。

这是一篇伟大的文章,讨论了集成测试相对于单元测试的好处;单元测试击杀"(上面写着)