【问题标题】:Unit test of Spring boot service with private fields and @PostConstruct使用私有字段和@PostConstruct 对 Spring 引导服务进行单元测试
【发布时间】:2018-10-16 10:07:56
【问题描述】:

我有一个这样定义的 Spring 引导服务

@Service
public class MyService {
    private String field1;
    private String field2;

    @Autowired
    private AnotherService anotherService

    @PostConstruct
    public void init() {
        anotherService.initField1(field1);
        anotherService.initField2(field2);
    }

    public String foo() {
        return field1 + field2;
    }
}

我应该如何为foo 编写单元测试。嗯,更多的是关于如何处理类字段和PostConstruct 方法。

谢谢!!

编辑: 还添加了AnotherService 作为字段。

【问题讨论】:

  • 为什么你认为你需要一个 PostConstruct?为什么不能使用普通的构造函数?
  • 这是一项服务。为什么需要构造函数?那么,即使我使用构造函数,我该如何编写单元测试呢?

标签: java unit-testing spring-boot


【解决方案1】:

以下示例显示了一个@Service Bean,它使用构造函数注入来获取所需的AnotherService bean:

@Service
public class MyService {
    private String field1;
    private String field2;

    private final AnotherService anotherService;

    public MyService(AnotherService anotherService) {
        this.anotherService = anotherService;
        this.anotherService.initField1(field1);
        this.anotherService.initField2(field2);
    }

    public String foo() {
        return field1 + field2;
    }
}

请注意,您可以省略 @Autowired,因为 MyService 有一个构造函数。请参阅here 了解更多信息。

使用 Spring 进行测试
使用@RunWith(SpringRunner.class) 和@SpringBootTest 注入MyService 并开始使用它:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyServiceTest {
    @Autowired    
    private MyService service;

    @Test
    public void testFoo() {
        String expResult = "";
        String result = service.foo();
        assertEquals(expResult, result);
    }
}

没有 Spring 的测试

public class MyServiceTest2 {
    private MyService service;

    @Before
    public void setUp() {
        service = new MyService(new AnotherService.Fake());
    }

    @Test
    public void testFoo() {
        String expResult = "";
        String result = service.foo();
        assertEquals(expResult, result);
    }
}

这里的FakeAnotherService 接口的假实现,它允许您进行纯单元测试。

【讨论】:

  • 这不是使用SpringRunner,那如果我依赖另一个服务呢?让我为这个问题添加更多内容。
  • 这是关于单元测试的。 @SpringBootTest 是关于启动一个容器进行集成测试。这当然并不意味着它不会工作,
  • @DoeJohnson 你是对的,添加了一个纯单元测试的新示例。
【解决方案2】:

编写好的、可测试的代码可能很难。有些陷阱等着大家迟早会掉进去。

根据经验,尽量避免字段级注入,改用构造函数参数注入:

@Service
public class MyService {

    private AnotherService anotherService;

    @Autowired
    MyService (AnotherService anotherService) {
         this.anotherService = anotherService;
    }

}

这是最干净的解决方案。您可以从测试中调用构造函数,spring 将在运行时以相同的方式注入依赖项。所以没有区别处理。

对于像@PostConstruct 这样的任何生命周期结构也是如此。如果你能避免它们,那就去做吧。让构造函数处理它。如果您绝对必须保留它们,那么唯一合乎逻辑的解决方案是从您的测试代码中手动调用它们。

现在,如何设置在运行时由容器自动装配的服务?

对于单元测试,您基本上有三个选项(不分先后):

  1. 如果所需的服务相当简单并且可以轻松构建,请像框架一样创建并传递它。

  2. 如果服务的接口有限且不会经常更改,请创建假服务。

  3. 使用 mockito 之类的模拟库(spring-boot-test 默认提供)。

【讨论】:

    猜你喜欢
    • 2018-11-09
    • 1970-01-01
    • 2019-09-16
    • 1970-01-01
    • 2018-02-01
    • 2016-10-25
    • 1970-01-01
    • 1970-01-01
    • 2014-04-01
    相关资源
    最近更新 更多