Java 当您与dummy&;有接口时,单元测试的最佳方法是什么;真正的实现?

Java 当您与dummy&;有接口时,单元测试的最佳方法是什么;真正的实现?,java,unit-testing,interface,junit,tdd,Java,Unit Testing,Interface,Junit,Tdd,我熟悉TDD的基本原则,即: 编写测试时,由于没有实现,这些测试将失败 编写基本实现以使测试通过 重构代码 然而,对于接口和实现适合哪里,我有点困惑。我在业余时间创建了一个Spring web应用程序,我想了解如何更好地测试接口/实现,而不是大刀阔斧,以我在这里创建的这个简单示例代码为例: public class RunMe { public static void main(String[] args) { // Using a dummy service

我熟悉TDD的基本原则,即:

  • 编写测试时,由于没有实现,这些测试将失败
  • 编写基本实现以使测试通过
  • 重构代码
  • 然而,对于接口和实现适合哪里,我有点困惑。我在业余时间创建了一个Spring web应用程序,我想了解如何更好地测试接口/实现,而不是大刀阔斧,以我在这里创建的这个简单示例代码为例:

    public class RunMe
    {
    
        public static void main(String[] args)
        {
            // Using a dummy service now, but would have a real implementation later (fetch from DB etc.)
            UserService userService = new DummyUserService();
            System.out.println(userService.getUserById(1));
        }
    }
    
    interface UserService
    {
        public String getUserById(Integer id);
    }
    
    class DummyUserService implements UserService
    {
        @Override
        public String getUserById(Integer id)
        {
            return "James";
        }
    }
    
    我已经创建了
    UserService
    接口,最终会有一个查询数据库的真正实现,但是为了让应用程序启动,我已经用一个
    DummyUserService
    实现代替了,它现在只返回一些静态数据

    问题:如何实施上述测试策略?

    我可以创建一个名为
    DummyUserServiceTest
    的测试类,并测试当我调用
    getUserById()
    时,它将返回
    James
    ,如果不浪费时间的话,看起来非常简单(?)

    随后,我还可以创建一个测试类
    RealUserService
    ,该类将测试
    getUserById()
    从数据库返回用户名。这是一个让我有点困惑的部分,在这样做的过程中,这是否本质上超出了单元测试的边界,并且变得更像是一个集成测试(在DB上有点击)

    问题(改进了一点):当使用带有虚拟/存根和真实实现的接口时,哪些部分应该进行单元测试,哪些部分可以安全地不进行测试?


    昨晚我花了几个小时在谷歌上搜索这个话题,主要是找到了关于什么是TDD的教程,或者是如何使用JUnit的示例,但在建议实际应该测试什么方面却没有找到任何东西。但完全有可能的是,我搜索不够努力,或者没有找到正确的东西……

    不要测试虚拟实现:它们不会在生产中使用。对它们进行测试是没有实际意义的

    如果real UserService实现只需转到数据库并通过其ID获取用户名,那么测试应该测试它是否做到了这一点,并且是否正确。如果您愿意,可以称之为集成测试,但它仍然是一个应该编写并自动化的测试


    通常的策略是在测试的@Before注释方法中用最少的测试数据填充数据库,并让您的测试方法检查数据库中存在的ID是否返回了相应的用户名。

    JB的答案很好,我想我应该放弃我使用的另一种技术

    在开发原始测试时,首先不要费心删除
    UserService
    。事实上,继续写真实的东西吧。按照下面的步骤进行

    1) 让它发挥作用。 2) 把它做好。 3) 快点

    您的代码将进行测试,然后验证findby id是否有效。正如JB所述,此时您的测试将被视为集成测试。一旦他们通过,我们就成功地完成了第一步。现在,看看设计。是这样吗?调整任何设计气味,并从列表中勾选第2步

    对于第3步,我们需要快速进行此测试。我们都知道,在所有事务管理和数据库设置中,集成测试都很慢,而且容易出错。一旦我们知道了代码的工作原理,我通常就不必担心集成测试了。此时,您可以引入虚拟服务,有效地将集成测试转化为单元测试。现在它没有以任何方式接触数据库,我们可以从列表中勾选步骤3,因为这个测试现在很快

    那么,这种方法有什么问题?很多人会说,我仍然需要对数据库支持的
    UserService
    进行测试。我通常不会在项目中进行集成测试。我的观点是,这些类型的测试是缓慢的、脆弱的,并且在大多数项目中没有捕捉到足够的逻辑错误来为自己买单

    希望有帮助


    布兰登

    我建议你先读这本书:史蒂夫·弗里曼和纳特·普赖斯写的。它回答了您和其他许多与TDD相关的问题

    在您的特定情况下,您应该使用DB适配器配置您的
    RealUserService
    ,这将进行真正的DB查询。服务本身将支持服务,而不是数据持久性。读这本书,会有很大帮助:)