为什么我的Spring Boot Service测试返回NullPointerException?

时间:2018-07-27 18:45:46

标签: java spring-boot junit

最近我一直在尝试测试Spring Boo(2.0.3)服务,但是我一直在收到NullPointerException。 userDAOImpl实现了userDAO接口,

这是serviceImpl类,它实现了Service接口:

import app.clothapp.DAO.UserDAO;
import app.clothapp.Model.User;
import app.clothapp.Service.UserService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class UserServiceImpl implements UserService {
    Logger logger = LogManager.getLogger("UserServiceImplLogger");


    @Autowired
    private UserDAO userDao;



    @Override
    @Transactional
    public void createUser(User user) {
        logger.debug("Creating an user " + UserServiceImpl.class.getName());
        userDao.addUser(user);
    }

    @Override
    public void deleteUserById(int id) {
        logger.debug("Deleting an user " + UserServiceImpl.class.getName());
        userDao.deleteUserById(id);
    }

    @Override
    public List<User> getUsersByEmail(String email) {
        logger.debug("Getting a list of users by email" + UserServiceImpl.class.getName());
        return userDao.findUsersByEmail(email);
    }

    @Override
    public void changeUserPassword(User user, String newPassword) {
        logger.debug("Changing a user's password " + UserServiceImpl.class.getName());
        userDao.changeUserPassword(user, newPassword);
    }

    @Override
    public void changeUserFirstName(User user, String firstName) {
        logger.debug("Changing an user's first name" + UserServiceImpl.class.getName());
        userDao.changeUserFirstname(user, firstName);
    }

    @Override
    public void changeUserLastname(User user, String lastname) {
        logger.debug("Changing an user's last name" + UserServiceImpl.class.getName());
        userDao.changeUserLastname(user, lastname);
    }

    @Override
    public void changeUserEmail(User user, String email) {
        logger.debug("Changing an user's email" + UserServiceImpl.class.getName());
        userDao.changeUserEmail(user, email);
    }

    @Override
    public List<User> findAllUsers() {
        logger.debug("Finding all users" + UserServiceImpl.class.getName());
        return userDao.findAllUsers();
    }
}

这是我的UserDAOImpl。

import app.clothapp.DAO.UserDAO;
import app.clothapp.Model.User;
import app.clothapp.Model.User.UserFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

//import static org.apache.logging.log4j.Level.ALL;
//import static org.apache.logging.log4j.Level.DEBUG;
//import static org.apache.logging.log4j.Level.WARN;




@Repository
@Qualifier("UserDAO")
public class UserDAOImpl implements UserDAO {


    @Autowired
    JdbcTemplate jdbcTemplate;
    Logger logger = LogManager.getLogger("Log");



    @Override
    public void addUser(User user) {
        logger.debug(UserDAOImpl.class.getName() + ": addUser() creating user");
        if (findUsersByEmail(user.getEmail()).isEmpty() == false) {
            logger.debug(UserDAOImpl.class.getName() + ":addUser() denied user creation because of email duplicate!");
        } else {
            jdbcTemplate.update("INSERT INTO User(firstName, lastName, Email, Password) VALUES (?, ?, ?, ?) ", user.getFirstname(), user.getLastname(), user.getEmail(), user.getPassword());
            logger.debug(UserDAOImpl.class.getName() + ": addUser() User created!");

        }

    }

    @Override
    public void changeUserPassword(User user, String password) {
        jdbcTemplate.update("UPDATE User SET Password = ? WHERE Id = ?", password, user.getId());
        logger.debug(UserDAOImpl.class.getName() + "User changed password. ");
    }

    @Override
    public void changeUserFirstname(User user, String firstName) {
        jdbcTemplate.update("UPDATE User SET Firstname = ? WHERE Id = ?", firstName, user.getId());
        logger.debug(UserDAOImpl.class.getName() + "User changed the first name.");
    }

    @Override
    public void changeUserLastname(User user, String lastname) {
        jdbcTemplate.update("UPDATE User SET Lastname = ? Where Id = ?", lastname, user.getId());
        logger.debug(UserDAOImpl.class.getName() + "User changed last name.");
    }

    @Override
    public void changeUserEmail(User user, String email) throws RuntimeException {
        if (findUsersByEmail(email).isEmpty() == false) {
            System.out.println(UserDAOImpl.class.getName() + "There is already a user within the database that has that email.");
            throw new RuntimeException(UserDAOImpl.class.getName() + "There is already a user within the database that has that email.");
        } else {
            jdbcTemplate.update("UPDATE User SET Email = ? Where Id = ?", email, user.getId());
            logger.debug(UserDAOImpl.class.getName() + new StringBuilder().append("User").append(user.getFirstname()).append("email was changed.").toString());
        }
    }

    @Override
    public void deleteUserById(int id) {
        logger.debug("Deleted user with user ID" + id + UserDAOImpl.class.getName());
        jdbcTemplate.update("DELETE from User WHERE Id = ?", id);
    }

    @Override
    public List<User> findUsersByEmail(String email) {
        logger.debug(UserDAOImpl.class.getName() + "found users by email using foundUsersByEmail()");
        return jdbcTemplate.query("SELECT * FROM User where Email = ?", new UserRowMapper(), email);
    }


    @Override
    public List<User> findAllUsers() {
        logger.debug(UserDAOImpl.class.getName() + "found all Users");
        return jdbcTemplate.query("SELECT * FROM User", new UserRowMapper());

    }

    public class UserRowMapper implements RowMapper<User> {
        public User mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
            UserFactory userFactory = new User.UserFactory()
                    .setId(resultSet.getInt("id"))
                    .setFirstname(resultSet.getString("Firstname"))
                    .setLastname(resultSet.getString("Lastname"))
                    .setPassword(resultSet.getString("Password"))
                    .setEmail(resultSet.getString("Email"));

            User user = userFactory.build();
            return user;
        }
    }
}

这是我的JUnit 5 Spring Boot测试:

@RunWith(SpringJUnit4ClassRunner.class)

class UserServiceTest {

User user = new User.UserFactory().setFirstname("Firs")
        .setLastname("Las")
        .setEmail("email@email.com")
        .setPassword("paaaaaaa")
        .build();

@Mock
private JdbcTemplate jdbcTemplate = new JdbcTemplate();

@InjectMocks
private UserServiceImpl userService;


@Mock
private UserDAO userDAO;

@BeforeEach
void setUp() {

    MockitoAnnotations.initMocks(this.getClass());



}

@Test
void createUser() {
    Mockito.doNothing().when(userDAO).addUser(user);
    userService.createUser(user);

    Mockito.when(userService.findAllUsers().get(0)).thenReturn(user);

    assertEquals(userService.findAllUsers().get(0), user);
}

@Test
void deleteUserById() {
}

@Test
void getUsersByEmail() {
}

@Test
void changeUserPassword() {
}

@Test
void changeUserFirstName() {
}

@Test
void changeUserLastname() {
}

@Test
void changeUserEmail() {
}

@Test
void findAllUsers() {
}

}

如您所见,我利用UserFactory创建了一个用户,模拟了jdbcTemplate和userDAOImpl,然后将它们注入UserServiceImpl()。在createUser()测试中,我使用服务创建用户,并设置Mockito.when,然后测试用户是否在数据库中。

但是,我不确定为什么会收到NullPointerException。有人可以给我一个提示吗?

编辑:我现在收到一个NullInsteadOfMock错误,我必须实例化UserDAO对象,但是UserDAO是一个接口!

org.mockito.exceptions.misusing.NullInsteadOfMockException: 
Argument passed to when() is null!
Example of correct stubbing:
    doThrow(new RuntimeException()).when(mock).someMethod();
Also, if you use @Mock annotation don't miss initMocks()

    at app.clothapp.ServiceImpl.UserServiceTest.createUser(UserServiceTest.java:55)
    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.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:513)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:170)
    at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:166)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:113)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:113)
    at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:121)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
    at java.util.Iterator.forEachRemaining(Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:121)
    at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:121)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
    at java.util.Iterator.forEachRemaining(Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:121)
    at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:55)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

2 个答案:

答案 0 :(得分:1)

您的Mockito代码有点差。而不是使用UserServiceImpl实例化new,您应该只使用InjectMocks批注。 UserDao应该被模拟,而不是UserDaoImpl。在调用UserService.createUser之前,应该为UserDao.createUser设置一个模拟,使其在被调用时不执行任何操作。

答案 1 :(得分:0)

要使用@Mock批注,您需要使用

@RunWith(MockitoJUnitRunner.class)

代替

@RunWith(SpringJUnit4ClassRunner.class)

因此,您的模拟游戏是正确创建的。

也在这一行:

@Mock
private JdbcTemplate jdbcTemplate = new JdbcTemplate();

您需要删除新的内容,因为您希望mockito创建和管理该类的模拟实例。像这样:

@Mock
private JdbcTemplate jdbcTemplate;