'Spring boot @Autowired not working in unit test case

As I understand that if we use spring stereotypes then we don't need to use new keyword to create an instance. Spring manages that for us and provide us with the beans at runtime.

And in order for Spring to inject those beans we need to use @Autowired annotation where we want Spring to inject that bean. Below I have a very simple class where I am using @Component so that spring manages that. This class has one List which I am initializing with my own responsibility and then a small method which does some logic.

@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); });
    }

}

I also wrote one unit test to test that and here is that.

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"));

    }
}

The test fails with

java.lang.NullPointerException when it tries to call parseStrings method which means that its not able to inject a proper initialized bean at run time.

Can some one guide that what I am missing? Is it necessary to add constructors (which here I am doing using lombok annotations) when using spring stereotypes on a class.



Solution 1:[1]

I don't see any mock created so why you are using @RunWith(MockitoJUnitRunner.class)?

I've seen as well answers recommending the use of @SpringBooTest. This annotation loads the whole context of your application basically for integration tests in order to integrate different layers of the application. That also means no mocking is involved. Do you really need that? (I don't think so since you're talking about unit test)

If your parser doesn't reference any other Bean (which need to be mocked), then you are in case of simple unit test.

@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"));

    }

Solution 2:[2]

Spring Autowire if you run the test case with SpringRunner. So modify the test class as follows.

@RunWith(SpringRunner.class)
class ParserTest {

}

To answer your second question, No, it is not necessary to add no-argument constructor unless you also have a parameterised constructor in the same class. In that case you need to explicitly add a no-arg constructor.

Solution 3:[3]

Why so you even need MockitoJUnitRunner here? The Parser has no dependencies. A simple initialization in the test will be enough. Just inialize the Parser instead of using an annotation. @SpringBootTest is meant for integration tests. It brings in the Spring context and makes your unit test slow & bulky.

Solution 4:[4]

In my case the class was not declared public

Solution 5:[5]

This should work:

@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"));

    }
}

Solution 6:[6]

SpringBoot2,you can use the annotation:@SpringBootTest to do unit test.

just like below case:

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

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Pradyskumar
Solution 3 Steven Diamante
Solution 4 user2579273
Solution 5 Umesh Sanwal
Solution 6 Dimple