Spring 启动@Autowired 在单元测试用例中不工作

Spring boot @Autowired not working in unit test case

据我了解,如果我们使用 spring 构造型,那么我们不需要使用 new 关键字来创建实例。 Spring 为我们管理并在 运行 时间为我们提供 bean。

为了让 Spring 注入那些 bean,我们需要在我们希望 Spring 注入那个 bean 的地方使用 @Autowired 注释。 下面我有一个非常简单的 class,我在其中使用 @Component,以便 spring 进行管理。这个 class 有一个我自己负责初始化的列表,然后是一个执行一些逻辑的小方法。

@Slf4j
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
public class Parser {

    private List<String> strList = new ArrayList<>();

    public void parseStrings(final String[] strs) {

        Arrays.stream(strs)
                .map(String::toLowerCase)
                .filter(str -> str.length() > 8)
                .filter(str -> str.endsWith("sam"))
                .forEach(sam1 ->  { strList.add(sam1); });
    }

}

我还写了一个单元测试来测试它,就是这个。

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.*;
@RunWith(MockitoJUnitRunner.class)
class ParserTest {

   @Autowired
   private Parser parser;

    @Test
    void parseStrings() {
        String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
        parser.parseStrings(str);

        assertTrue(parser.getStrList().size() == 3);
        assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));

    }
}

测试失败

java.lang.NullPointerException 当它尝试调用 parseStrings 方法时,这意味着它无法在 运行 时间注入正确的初始化 bean。

有人可以指导我缺少什么吗? 在 class.

上使用 spring 构造型时是否有必要添加构造函数(我在这里使用 lombok 注释)

Spring Autowire 如果你 运行 测试用例 SpringRunner。所以修改测试class如下

@RunWith(SpringRunner.class)
class ParserTest {

}

为了回答你的第二个问题, 不,没有必要添加 no-argument constructor,除非您在同一个 class 中也有一个 parameterised constructor。在这种情况下,您需要显式添加无参数构造函数。

SpringBoot2,可以使用注解:@SpringBootTest做单元测试

就像下面的案例:

@SpringBootTest
class DemoApplicationTests {
   
    @Test
    void contextLoads() {
    }

}

我没有看到创建任何模拟,所以你为什么要使用 @RunWith(MockitoJUnitRunner.class)

我也看到了推荐使用 @SpringBooTest 的答案。此注解加载应用程序的整个上下文,主要用于集成测试,以便集成应用程序的不同层。这也意味着不涉及模拟。你真的需要吗? (我不这么认为,因为你在谈论单元测试)

如果你的解析器没有引用任何其他 Bean(需要被模拟),那么你就是在进行简单的单元测试。

@RunWith(SpringRunner.class) // you can even removed it 
class ParserTest {

private Parser parser;

@Before
public void setUp() {
     parser = new Parser();

}

    @Test
    void parseStrings() {
        String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
        parser.parseStrings(str);

        assertTrue(parser.getStrList().size() == 3);
        assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));

    }

这应该有效:

@SpringBootTest
@RunWith(SpringRunner.class)
class ParserTest {

   @Autowired
   private Parser parser;

    @Test
    void parseStrings() {
        String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
        parser.parseStrings(str);

        assertTrue(parser.getStrList().size() == 3);
        assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));

    }
}

为什么你在这里甚至需要 MockitoJUnitRunner?解析器没有依赖关系。在测试中进行简单的初始化就足够了。只需初始化解析器而不是使用注释。 @SpringBootTest 用于集成测试。它引入 Spring 上下文并使您的单元测试缓慢且庞大。

在我的例子中,class 声明为 public