8

The following test fails with a NullPointerException on the line usersRepo.save(user);. I believe it's because when the test goes into the performProvision() function the usersRepo object is null.

However, when the web service is actually running and the endpoint for my controller is hit, everything works fine and the database is updated.

Any idea why the test fails? My idea was that PAutoProvision references the real database, whereas it should be dealing with the in-memory database so maybe there is some sort of conflict? I have also seen a lot of different examples with annotations set up differently, so I suppose that could be a problem too.

UsersRepo extends JpaRepository where PAutoProvision is a SQL table entity.

If this isn't enough information, I can provide the UsersRepo, PAutoProvision, and ProvisionController classes if necessary.

The Service:

@Service
public class ProvisionService {

    @Autowired
    private UsersRepo usersRepo;


    public String performProvision(UserData userData) {
        try {
            PAutoProvision user = new PAutoProvision(userData);
            usersRepo.save(user);  // OOTB CRUD repository functionality of springData to update/insert record data
            return String.format("Success: User %s has been added to the database", userData.getUserId());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.toString());
            System.out.println("\n\n Cannot perform the provisioning of the user " + userData.getUserId() + ": \n" + e.toString() + "\n\n");
        }

        return "problem";
    }
}

The Test:

@RunWith(SpringRunner.class)
public class ProvisionServiceTest {

    private ProvisionService provisionService;

    @MockBean
    private UsersRepo usersRepo;

    @Before
    public void setUp(){
        provisionService = new ProvisionService();
    }

     @Test
    public void performProvision_shouldPass() {
        UserData userData = new UserData("userid", 30, 1, "spot", 1);
        try {
            String result = provisionService.performProvision(userData);
            assertThat(result, is(equalTo("Success: User userid has been added to the database")));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }
    }
}

EDIT 1:

Adding @Autowired and removing setUp() method results in the following:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.autoprovision.ProvisionServiceTest': Unsatisfied dependency expressed through field 'provisionService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.autoprovision.ProvisionService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Darrel Holt
  • 870
  • 1
  • 15
  • 39
  • 4
    You're creating the ProvisionService instance yourself, using `new`. So Spring can't possibly know about this service instance, and will not inject anything in it. If you want Spring to inject the repo into your service, you need to let Spring create the service, and inject it into your test: `@Autowired private ProvisionService provisionService;` (and no setUp method). – JB Nizet Jun 30 '17 at 19:09
  • @JBNizet See my edit – Darrel Holt Jul 03 '17 at 15:25
  • 1
    That is not sufficient. You also need to tell Spring which context to load: what are the beans to load, where are they defined, etc. Or to let spring boot auto-discover your context configuration. Why don't you read the documentation? https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications That said, I don't even understand why you're using a spring test here, instead of using constructor injection, creating a mock repo using Mockito, and creating the service by calling its constructor with the mock repo as argument. No Spring needed – JB Nizet Jul 03 '17 at 15:42
  • The way I've been told to set up a mock repo at `/in/memory/database.sql` and access it using `@Sql({"/in/memory/database.sql"})` in my repo test. Is that along the same lines of what you meant at the end of your comment? – Darrel Holt Jul 03 '17 at 16:05
  • After help from you and @iBiber I have learned that what I'm trying to do is a repository test rather than a service test -- I wasn't designing the service to be modular enough. My solution was removing the test from the service test class, drop the test in the repo test class, and break it into sub-tests to test the parts of what `performProvision_shouldPass()` was trying to test all at once. – Darrel Holt Jul 03 '17 at 16:17
  • No. If you're testing the service, all you need is a mock repo. You would need a database, containing data, if you want to test your repo. – JB Nizet Jul 03 '17 at 16:42

2 Answers2

5

As JB Nizet already stated, the UserRepo mock is not injected into the provisionService instance, because the provisionService instance is created in the setUp method with new.

Your test should look like this:

@RunWith(SpringRunner.class)
public class ProvisionServiceTest {
    @Autowired // let Spring instantiate the instance to test
    private ProvisionService provisionService;

    @MockBean
    private UsersRepo usersRepo;

    @Test
    public void performProvision_shouldPass() {
        UserData userData = new UserData("userid", 30, 1, "spot", 1);

        String result = provisionService.performProvision(userData);
        assertThat(result, is(equalTo("Success: User userid has been added to the database")));
    }
}
iBiber
  • 172
  • 4
  • I originally had it `@Autowired` and no `setUp()` method. See my edit in the OP for what happens when I change to to yours. – Darrel Holt Jul 03 '17 at 15:25
3

The given answer seems useful but just in case it can help someone, testing service is not so obvious in Spring (IMHO). The SpyBean annotation turns out to be really handy:

@RunWith(SpringRunner.class)
public class ProvisionServiceTest {
    @SpyBean
    private ProvisionService provisionService;
}

Source : Spring Boot Reference - Testing

Robin
  • 1,438
  • 2
  • 19
  • 29