【问题标题】:@Repository instance is null when invoking a method using a @Service instance from a unit test使用单元测试中的 @Service 实例调用方法时,@Repository 实例为空
【发布时间】:2020-07-10 11:44:41
【问题描述】:

我的目标是为这些单元测试使用内存数据库,这些依赖项被列为:

implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("com.h2database:h2")

这样存储库实例实际上与数据库交互,我不只是模拟返回值。

问题是当我运行我的单元测试时,服务实例中的存储库实例是null

这是为什么呢?我是否缺少单元测试类上的一些注释来初始化存储库实例?

这是运行我的单元测试时的控制台输出:

null

java.lang.NullPointerException
    at com.my.MyService.findAll(MyService.java:20)
    at com.my.MyTest.testMy(MyTest.java:23)

我的单元测试类:

public class MyTest {

  @MockBean
  MyRepository myRepository;

  @Test
  void testMy() {
    MyService myService = new MyService();
    int size = myService.findAll().size();
    Assertions.assertEquals(0, size);
  }
}

我的服务等级:

@Service
public class MyService {

    @Autowired
    MyRepository myRepository;

    public List<MyEntity> findAll() {

        System.out.println(myRepository); // null
        return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
    }

    @Transactional
    public MyEntity create(MyEntity myEntity) {

        myRepository.save(myEntity);

        return myEntity;
    }
}

我的存储库类:

@Repository
public interface MyRepository extends CrudRepository<MyEntity, Long> {

}

我的实体类:

@Entity
public class MyEntity {

    @Id
    @GeneratedValue
    public Long id;
}

【问题讨论】:

    标签: java spring spring-boot unit-testing jpa


    【解决方案1】:

    这是为什么呢?我是否缺少单元测试类上的一些注释来初始化存储库实例?

    基本上是的:)

    您需要通过使用 @SpringBootTest 注释您的测试类来初始化 Spring 上下文

    您遇到的另一个问题是您手动创建了MyService 对象。 这样一来,SpringBoot 就没有机会为您注入任何 Bean。您可以通过在您的测试类中简单地注入您的MyService 来解决此问题。您的代码应如下所示:

    @SpringBootTest
    public class MyTest {
    
        @Autowired
        private MyService myService;
    
        @Test
        void testMy() {
            int size = myService.findAll().size();
            assertEquals(0, size);
        }
    }
    

    【讨论】:

      【解决方案2】:

      要使用@MockBean注解,你必须使用SpringRunner来运行测试。在您的测试类之上使用@RunWith 注解并通过SpringRunner.class

      @RunWith(SpringRunner.class)
      public class MyTest {
      
        @MockBean
        MyRepository myRepository;
      
        @Test
        void testMy() {
          MyService myService = new MyService();
          int size = myService.findAll().size();
          Assertions.assertEquals(0, size);
        }
      }
      

      【讨论】:

        【解决方案3】:

        这里的问题是您的服务实现。使用@Autowired 注入依赖项将在您运行整个应用程序时起作用,但它不允许您在需要时注入自定义依赖项,测试就是一个很好的例子。

        将您的服务实现更改为:

        @Service
        public class MyService {
        
            private MyRepository myRepository;
        
            public MyService(MyRepository myRepository){
                this.myRepository = myRepository;
            }
        
            public List<MyEntity> findAll() {
        
                System.out.println(myRepository); // null
                return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
            }
        
            @Transactional
            public MyEntity create(MyEntity myEntity) {
        
                myRepository.save(myEntity);
        
                return myEntity;
            }
        }
        

        这个构造函数会被spring调用。然后将您的测试更改为:

        public class MyTest {
        
          @Mock
          MyRepository myRepository;
        
          @Test
          void testMy() {
            MyService myService = new MyService(myRepository);
            int size = myService.findAll().size();
            Assertions.assertEquals(0, size);
          }
        }
        
        

        请注意,我已将 @MockBean 替换为 @Mock,因为前面的注释用于将模拟 bean 注入到 spring 上下文中,如果您正在进行单元测试,则不需要。如果你想启动 spring 上下文(我不推荐你),你需要使用@SpringBootTest 或其他一些可用的替代方法来配置你的测试类。这会将您的测试转换为集成测试。

        PD:如果您不向 myRepository.findAll() 提供模拟,则此测试将不起作用。 Mockito 默认行为是返回 null,但您希望它返回 0,因此您需要执行类似 given(myRepository.findAll()).willReturn(0) 的操作。

        【讨论】:

        • 感谢您提供的信息。但我确实概述了我希望做一个集成风格的测试,因此我不会模拟myRepository.findAll() 的响应。我希望对内存数据库进行适当的查询以返回 0/null 的结果 .. 因为此时我还没有将任何对象插入到数据库中。
        【解决方案4】:

        我相信您希望编写集成测试。在这里,您可以删除 MockBean 注释并简单地自动装配您的存储库。另外,使用 SpringRunner 类运行。

        @RunWith(SpringRunner.class)
        public class MyTest {
        
          @Autowired
          MyRepository myRepository;
        
          @Autowired
          MyService myService
          
          @Test
          void testMy() {
           int size = myService.findAll().size();
           Assertions.assertEquals(0, size);
          }
        }
        

        这应该可以工作

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-10-16
          • 1970-01-01
          • 2021-11-28
          • 2023-01-27
          • 2016-08-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多