英文:
How to unit-test a class which has methods using database connection?
问题
我正尝试在一个controller
类上运行测试,它至少有一个方法内部使用DAO
来从DB(MySQL)
检索信息。
我遇到的问题是dao
方法返回null
,我最终得到NullPointerException
错误。
如何测试一个类,它的方法在内部使用数据库连接?<br>
我没有找到任何有用的帖子/答案。
项目结构:<br>
<pre>
src
main
java
[package]
RegisterController.java
Config.java // 配置类
test
java
[package]
RegisterControllerTest.java
</pre>
RegisterController.java
package com.webapp.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.webapp.Config;
import com.webapp.dao.AccountDao;
@Controller
public class RegisterController {
@Autowired
AccountDao accDao;
public String validateUsername(String uname){
List<String> errors = new ArrayList<String>();
// ... 与问题无关的代码
// 在这里抛出 NullPointerException
if(accDao.getAccountByUsername(uname) != null)
errors.add("err#taken");
return errors.toString();
}
}
RegisterControllerTest.java
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import com.webapp.controller.RegisterController;
import com.webapp.Config;
@SpringBootTest
public class RegisterControllerTest {
@Mock
private Config config;
private RegisterController rc;
@BeforeEach
public void init() {
config = Mockito.mock(Config.class);
rc = new RegisterController();
}
@Test
public void testValidateUsername() {
assertEquals("[]", rc.validateUsername("Username123")); // N.P.E
}
}
Config.java
package com.webapp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import com.webapp.dao.AccountDao;
import com.webapp.dao.AccountDaoImpl;
import com.webapp.dao.Dao;
@Configuration
@ComponentScan(basePackages = { "com.webapp.controller", "com.webapp.dao", "com.webapp.test" })
public class Config {
private static class Database {
private static String host = "127.0.0.1";
private static String user = "root";
private static String pass = "root";
private static String dbname = "memedb";
private static int port = 3306;
public static String getUrl() {
return "jdbc:mysql://" + host + ":" + port + "/" + dbname + "?serverTimezone=Europe/Stockholm";
}
}
@Bean
public DriverManagerDataSource getDataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl(Database.getUrl());
ds.setUsername(Database.user);
ds.setPassword(Database.pass);
return ds;
}
@Bean
public AccountDao getAccDao() {
return new AccountDaoImpl(getDataSource());
}
}
英文:
I am trying to run tests on a controller
class and it has atleast a method which internally uses a DAO
to retrieve information from the DB (MySQL)
.
The problem I have is that the dao method is giving null and I end up with NullPointerException
error.
How do I tests a class which has methods that internally use a database connection?<br>
Haven't been able to find any useful post/answer.
Project structure:<br>
<pre>
src
main
java
[package]
RegisterController.java
Config.java // configuration class
test
java
[package]
RegisterControllerTest.java
</pre>
RegisterController.java
package com.webapp.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.webapp.Config;
import com.webapp.dao.AccountDao;
@Controller
public class RegisterController {
@Autowired
AccountDao accDao;
public String validateUsername(String uname){
List<String> errors = new ArrayList<String>();
// ... unrelated code
// NullPointerException thrown here
if(accDao.getAccountByUsername(uname) != null)
errors.add("err#taken");
return errors.toString();
}
}
RegisterControllerTest.java
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import com.webapp.controller.RegisterController;
import com.webapp.Config;
@SpringBootTest
public class RegisterControllerTest {
@Mock
private Config config;
private RegisterController rc;
@BeforeEach
public void init() {
config = Mockito.mock(Config.class);
rc = new RegisterController();
}
@Test
public void testValidateUsername() {
assertEquals("[]", rc.validateUsername("Username123")); // N.P.E
}
}
Config.java
package com.webapp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import com.webapp.dao.AccountDao;
import com.webapp.dao.AccountDaoImpl;
import com.webapp.dao.Dao;
@Configuration
@ComponentScan(basePackages = { "com.webapp.controller", "com.webapp.dao", "com.webapp.test" })
public class Config {
private static class Database {
private static String host = "127.0.0.1";
private static String user = "root";
private static String pass = "root";
private static String dbname = "memedb";
private static int port = 3306;
public static String getUrl() {
return "jdbc:mysql://"+host+":"+port+"/"+dbname+"?serverTimezone=Europe/Stockholm";
}
}
@Bean
public DriverManagerDataSource getDataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl(Database.getUrl());
ds.setUsername(Database.user);
ds.setPassword(Database.pass);
return ds;
}
@Bean
public AccountDao getAccDao() {
return new AccountDaoImpl(getDataSource());
}
}
答案1
得分: 1
代替对 config
的模拟,对 Dao
进行模拟。你遇到了 NPE,因为模拟的 config
没有设置 @Autowired accDao
... 所以你的 accDao == null
:
@Controller
public class RegisterController {
AccountDao accDao;
public RegisterController(AccountDao accDao) {
this.accDao = accDao;
}
...
}
@BeforeEach
public void init() {
accDaoMock = Mockito.mock(AccountDao.class);
rc = new RegisterController(accDaoMock);
}
@Test
public void testValidateUsername() {
when(accDaoMock.getAccountByUsername("Username123")).thenReturn(null);
assertEquals("[]", rc.validateUsername("Username123"));
}
英文:
Instead of mocking config
, mock the Dao
. You are getting NPE, because the mocked config is not setting @Autowired accDao
... so your accDao == null
:
@Controller
public class RegisterController {
AccountDao accDao;
public RegisterController(AccountDao accDao) {
this.accDao = accDao;
}
...
}
@BeforeEach
public void init() {
accDaoMock = Mockito.mock(AccountDao.class);
rc = new RegisterController(accDaoMock);
}
@Test
public void testValidateUsername() {
when(accDaoMock.getAccountByUsername("Username123")).thenReturn(null);
assertEquals("[]", rc.validateUsername("Username123"));
}
答案2
得分: 0
为什么要以编程方式配置数据库连接?我建议您使用Spring Boot的自动配置来配置数据库连接。
在单元测试中,DAO对象必须进行模拟。
这里 有一篇关于Spring Boot应用的JUnit测试的好文章。
英文:
Why are you configuring the connection DB programmatically? I advise you configure the connection DB with autoconfiguration of Spring Boot.
The DAO object have to mock in an unit test.
Here is a good article about the Junit test for a Spring Boot App.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论