【问题标题】:Correctly testing a Spring service [duplicate]正确测试 Spring 服务 [重复]
【发布时间】:2017-06-30 18:53:17
【问题描述】:

以下测试失败,NullPointerException 在行 usersRepo.save(user);。我相信这是因为当测试进入performProvision() 函数时,usersRepo 对象是null

但是,当 Web 服务实际运行并且我的控制器的端点被命中时,一切正常并且数据库已更新。

知道为什么测试失败了吗?我的想法是PAutoProvision 引用了真实的数据库,而它应该处理内存数据库,所以可能存在某种冲突?我还看到很多不同的示例,注释设置不同,所以我想这也可能是一个问题。

UsersRepo 扩展了 JpaRepository,其中 PAutoProvision 是一个 SQL 表实体。

如果这些信息还不够,我可以在必要时提供UsersRepoPAutoProvisionProvisionController 类。

服务:

@Service
public class ProvisionService {

    @Autowired
    private UsersRepo usersRepo;


    public String performProvision(UserData userData) {
        try {
            PAutoProvision user = new PAutoProvision(userData);
            usersRepo.save(user);  // OOTB CRUD repository functionality of springData to update/insert record data
            return String.format("Success: User %s has been added to the database", userData.getUserId());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.toString());
            System.out.println("\n\n Cannot perform the provisioning of the user " + userData.getUserId() + ": \n" + e.toString() + "\n\n");
        }

        return "problem";
    }
}

测试:

@RunWith(SpringRunner.class)
public class ProvisionServiceTest {

    private ProvisionService provisionService;

    @MockBean
    private UsersRepo usersRepo;

    @Before
    public void setUp(){
        provisionService = new ProvisionService();
    }

     @Test
    public void performProvision_shouldPass() {
        UserData userData = new UserData("userid", 30, 1, "spot", 1);
        try {
            String result = provisionService.performProvision(userData);
            assertThat(result, is(equalTo("Success: User userid has been added to the database")));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }
    }
}

编辑 1:

添加@Autowired 和删除setUp() 方法会产生以下结果:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.autoprovision.ProvisionServiceTest': Unsatisfied dependency expressed through field 'provisionService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.autoprovision.ProvisionService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

【问题讨论】:

  • 您正在使用new 自己创建 ProvisionService 实例。所以 Spring 不可能知道这个服务实例,也不会在其中注入任何东西。如果您希望 Spring 将 repo 注入您的服务,您需要让 Spring 创建服务,并将其注入您的测试:@Autowired private ProvisionService provisionService;(并且没有 setUp 方法)。
  • @JBNizet 查看我的编辑
  • 这还不够。您还需要告诉 Spring 要加载哪个上下文:要加载哪些 bean,它们在哪里定义等。或者让 Spring Boot 自动发现您的上下文配置。你为什么不阅读文档? docs.spring.io/spring-boot/docs/current/reference/htmlsingle/… 就是说,我什至不明白你为什么在这里使用弹簧测试,而不是使用构造函数注入,使用 Mockito 创建模拟 repo,并通过使用模拟 repo 作为参数调用其构造函数来创建服务。不需要弹簧
  • 我被告知在/in/memory/database.sql 建立一个模拟回购并在我的回购测试中使用@Sql({"/in/memory/database.sql"}) 访问它的方式。这是否与您在评论末尾的意思相同?
  • 在您和@iBiber 的帮助下,我了解到我正在尝试做的是存储库测试而不是服务测试——我没有将服务设计为足够模块化。我的解决方案是从服务测试类中删除测试,将测试放到 repo 测试类中,并将其分解为子测试,以测试 performProvision_shouldPass() 尝试一次性测试的部分。

标签: java spring hibernate junit spring-ws


【解决方案1】:

正如 JB Nizet 所说,UserRepo 模拟不会注入到 provisionService 实例中,因为 provisionService 实例是在 setUp 方法中使用new 创建的。

您的测试应如下所示:

@RunWith(SpringRunner.class)
public class ProvisionServiceTest {
    @Autowired // let Spring instantiate the instance to test
    private ProvisionService provisionService;

    @MockBean
    private UsersRepo usersRepo;

    @Test
    public void performProvision_shouldPass() {
        UserData userData = new UserData("userid", 30, 1, "spot", 1);

        String result = provisionService.performProvision(userData);
        assertThat(result, is(equalTo("Success: User userid has been added to the database")));
    }
}

【讨论】:

  • 我最初有它 @Autowired 并且没有 setUp() 方法。请参阅我在 OP 中的编辑,了解当我更改为您的时会发生什么。
【解决方案2】:

给定的答案似乎很有用,但以防万一它可以帮助某人,测试服务在 Spring 中并不那么明显(恕我直言)。 SpyBean 注释非常方便:

@RunWith(SpringRunner.class)
public class ProvisionServiceTest {
    @SpyBean
    private ProvisionService provisionService;
}

来源:Spring Boot Reference - Testing

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-09
    • 2021-03-15
    • 2017-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多