简体   繁体   English

@Autowired Junit 中的注入依赖项测试始终为 null

[英]@Autowired Injected dependency in Junit Test is always null

In a simple test class like the one below:在一个简单的测试 class 中,如下所示:

@SpringBootTest
@Slf4j
@ActiveProfiles("test")
public class LocalizerTest {

    @Autowired
    AddressInfoLocalizer addressInfoLocalizer;


    @Test
    public void localizeIp() {
        String ip = "8.8.8.8";
        Optional<CityResponse> response = addressInfoLocalizer.localize(ip);
        response.ifPresent(cityResponse -> log.info("{}", cityResponse));
    }

}

With the AddressInfoLocalizer (sorry for the strange name but I had to make it up) being basically like this: AddressInfoLocalizer (对不起,奇怪的名字,但我不得不弥补)基本上是这样的:


@Slf4j
@Component
public class AddressInfoLocalizer {

    @Value("${geolocalization.dbpath}")
    private String databasePath;

    private DatabaseReader database;

    @PostConstruct
    public void initialize() {
        log.info("GeoIP2 database path:{}", databasePath);
        File database = new File(databasePath);
        try {
            this.database = new DatabaseReader.Builder(database).build();
        } catch (IOException ioe) {
            this.database = null;
            log.warn("Problems encountered while initializing IP localization database, skipping resolutions");
        }

    }

    public Optional<CityResponse> localize(String ipAddressString) {
        if (isNull(database)) {
            log.warn("Attempted to resolve an IP location with database problems, skipping.");
            return Optional.empty();
        }
        try {
            InetAddress ipAddress = InetAddress.getByName(ipAddressString);
            return ofNullable(database.city(ipAddress));
        } catch (UnknownHostException uhe) {
            log.error("Unknown host {}, {}", ipAddressString, uhe.getCause());
            return Optional.empty();
        } catch (IOException ioe) {
            log.error("IO error while opening database, {}", ioe.getCause().toString());
            return Optional.empty();
        } catch (GeoIp2Exception gip2e) {
            log.error("GeoIP error, {}", gip2e.getCause().toString());
            return Optional.empty();
        }
    }
}

I keep getting a NullPointerException when calling (in the test) addressInfoLocalizer.localize(ip);调用(在测试中) addressInfoLocalizer.localize(ip); , ,

java.lang.NullPointerException
    at com.bok.parent.LocalizerTest.localizeIp(LocalizerTest.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

while debugging I can actually see that the addressInfoLocalizer object is null .在调试时,我实际上可以看到addressInfoLocalizer object 是null

I've created other classes the same way, but only this one seems to have that problem, what could be wrong?我以同样的方式创建了其他类,但似乎只有这个类有这个问题,有什么问题吗?

What I found out is that not always the @SpringBootTest is enough, in some classes/cases (to be honest I don't have a 100% clear idea so I'm not gonna say stupidities) it is needed that you manually choose the test-runner, like below:我发现@SpringBootTest 并不总是足够的,在某些类/情况下(老实说,我没有 100% 清晰的想法,所以我不会说愚蠢),需要手动选择测试运行器,如下所示:

@SpringBootTest
@RunWith(SpringRunner.class)
public class FooTest {
...
}

Keep in mind that if you decide to instantiate a dependency by yourself then Spring won't be able to inject all the needed classes, and then you'd need to change the runner into something like @RunWith(MockitoJUnitRunner.class)请记住,如果您决定自己实例化一个依赖项,那么 Spring 将无法注入所有需要的类,然后您需要将运行器更改为类似@RunWith(MockitoJUnitRunner.class)

(credits to JUNIT Autowired instance is always null ) (归功于JUNIT 自动装配实例始终为 null

Better approach is to use spring's underlying mock beans.更好的方法是使用 spring 的底层模拟 bean。

@SpringBootTest(classes= AddressInfoLocalizer.class)

this is actually the new recommended way.这实际上是新的推荐方式。

Two things here:这里有两件事:

  1. If you're testing, the ideal way is to mock instead of autowire(there can be exceptions)如果您正在测试,理想的方法是模拟而不是自动装配(可能有例外)
  2. You should use @ExtendWith(MockitoExtension.class) or @RunWith(MockitoJUnitRunner.class) so that your components will be loaded in spring context.您应该使用@ExtendWith(MockitoExtension.class)@RunWith(MockitoJUnitRunner.class)以便您的组件将在 spring 上下文中加载。 However ExtendWith is preferred way to initialize beans/components.然而ExtendWith是初始化 bean/组件的首选方式。 Because the later(RunWith) loads the entire spring context which might take more time因为后者(RunWith)加载整个 spring 上下文,这可能需要更多时间

So, your code should look something like:因此,您的代码应类似于:

@Slf4j
@ActiveProfiles("test")
@ExtendWith(MockitoExtension.class)
public class LocalizerTest {

    @Mock
    AddressInfoLocalizer addressInfoLocalizer;


    @Test
    public void localizeIp() {
        String ip = "8.8.8.8";
         //Mock here.
        Optional<CityResponse> response = addressInfoLocalizer.localize(ip);
        response.ifPresent(cityResponse -> log.info("{}", cityResponse));
    }

}

Reference/Read more: 参考/阅读更多:
https://www.baeldung.com/mockito-junit-5-extension https://www.baeldung.com/mockito-junit-5-extension
https://stackoverflow.com/questions/55276555/when-to-use-runwith-and-when-extendwith https://stackoverflow.com/questions/55276555/when-to-use-runwith-and-when-extendwith
https://www.javadoc.io/static/org.mockito/mockito-junit-jupiter/3.10.0/org/mockito/junit/jupiter/MockitoExtension.html https://www.javadoc.io/static/org.mockito/mockito-junit-jupiter/3.10.0/org/mockito/junit/jupiter/MockitoExtension.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM