Must use mockbean instead of autowired

2019-04-02 11:54发布

问题:

I use spring boot 2

I created a basic test

@RunWith(SpringJUnit4ClassRunner.class)
public class VehicleServiceImplTest {

    private VehiculeServiceImpl service;

    @Autowired
    private VehicleRepository repository;

    @Before
    public void prepare() {
        service = new VehiculeServiceImpl(repository);
    }

    @Test
    public void test(){

    }

}

I get

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.namur.service.VehicleServiceImplTest': Unsatisfied dependency expressed through field 'repository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.namur.repository.VehicleRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:586)

If i replace Autowired by MockBean it's working but I don't know why

回答1:

If i replace Autowired by MockBean it's working but I don't know why

It works because @MockBean replaces or adds a bean into the Spring context.
In your case, it adds a repository mock in the Spring context.
So this could not throw any UnsatisfiedDependencyException.

But that is not necessary what you need as you used initially @Autowired that is designed to inject a bean from the context.

@Autowired and @MockBean are indeed two very different things that you never substitute for a same need.


As a side note, you should probably rethink the way which your test is built.

Actually you are using the SpringJUnit4ClassRunner runner.
It means that you want to use the Spring container for your test.
It is a valid approach. But in this case, why do you create VehiculeServiceImpl outside the Spring container ?

 service = new VehiculeServiceImpl(repository);

You should rather inject the service.

Note that creating a new instance of the class under test outside the containers is also a very valid approach.
We do it as we write plain unit tests. If you want to do it, don't use the Spring Boot runner, that by the way, makes tests slower.



回答2:

It is because you have not supplied the test with any indication of what the spring context is hence there are no beans at all available to autowire.

By providing a @MockBean you are essentially providing a test context with a single existing bean which is a mock of the VehicleRepository class.

You can use the @SpringBootTest annotation which will load a spring context for you to use in your test. Then you should be able to @Autowire the actual repository:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class VehicleServiceImplTest {