如何对具有使用数据库连接的方法的类进行单元测试?

huangapple go评论57阅读模式
英文:

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&lt;String&gt; errors = new ArrayList&lt;String&gt;();

        // ... unrelated code 

        // NullPointerException thrown here
        if(accDao.getAccountByUsername(uname) != null)
            errors.add(&quot;err#taken&quot;);

        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(&quot;[]&quot;, rc.validateUsername(&quot;Username123&quot;)); // 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 = { &quot;com.webapp.controller&quot;, &quot;com.webapp.dao&quot;, &quot;com.webapp.test&quot; })
public class Config {

	private static class Database {
		private static String host = &quot;127.0.0.1&quot;;
		private static String user = &quot;root&quot;;
		private static String pass = &quot;root&quot;;
		private static String dbname = &quot;memedb&quot;;
		private static int port = 3306;

		public static String getUrl() {
			return &quot;jdbc:mysql://&quot;+host+&quot;:&quot;+port+&quot;/&quot;+dbname+&quot;?serverTimezone=Europe/Stockholm&quot;;
		}
	}
    
	@Bean
	public DriverManagerDataSource getDataSource() {
		DriverManagerDataSource ds = new DriverManagerDataSource();
		ds.setDriverClassName(&quot;com.mysql.cj.jdbc.Driver&quot;);
		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(&quot;Username123&quot;)).thenReturn(null);
  assertEquals(&quot;[]&quot;, rc.validateUsername(&quot;Username123&quot;));
}

答案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.

huangapple
  • 本文由 发表于 2020年4月5日 20:17:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/61042442.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定