JUnit&Mockito:如何将字段值注入 Spring 组件?
JUnit&Mockito: how to inject field values to Spring component?
我正在尝试测试 Spring @Repository
组件,但首先我必须注入 table 名称才能使其正常工作。
这是我做的。这是简单的 DAO:
@Repository
@Transactional
public class AccountDAO {
@Autowired
private JdbcTemplate jdbcTemplate;
private String table = "accounts"; <-- I need to inject this value from tests
...
}
单元测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-web-servlet.xml")
@Transactional
public class AccountDaoTest {
@Autowired
private AccountDAO accountDAO;
@Before
public void init() {
accountDAO = Mockito.spy(accountDAO);
ReflectionTestUtils.setField(accountDAO, "table", "accounts_test");
}
...
}
问题是 ReflectionTestUtils
忽略了字段分配并且 accountDAO
仍然使用 accounts
table 名称值而不是 accounts_test
。
我该如何解决这个问题?
您可以使用两种技术来更改存储在私有变量中的 table 名称:
ReflectionTestUtils
没有模拟间谍。
- 使用 Mockito 的注解@Spy 和@InjectMocks
但这不是非常可靠的集成测试,因为当有人更改 table
架构时,您的测试不会发现问题。有多种方法可以解决此类问题:
- 如果您的应用程序执行增量脚本以将数据库架构和元数据迁移到最新版本(例如通过使用 Liquibase 或 FlyWay),您可以重新创建数据库架构 in-memery/embedded 数据库(例如 H2、HSQL) 在测试期间和 运行 对照原始 table 进行测试。这是现代方法。
- 如果您的公司架构是老式的,一些 DBA 在您的应用程序部署中独立执行 SQL 增量脚本,则必须有一些 DEV 数据库环境。使用该环境进行集成测试。您可以整理 up/prepare 您的 table 以便预先测试。
在这里,您想要分离真实数据库和集成测试数据库。您的目标是测试 DAO 层是否运行良好。例如,我建议完全删除 Mockito 并使用 DBUnit。
然后,你保留你的 DAO,Spring 一个 JUnit。不需要注入另一个 table 名称。
DBUnit 允许您根据 XML 数据集创建仅用于测试的小型数据库。
像这样:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersistenceContext.class})
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
@DatabaseSetup("my-dbunit-dataset.xml")
public class AccountDAOTest {
@Autowired
private AccountDAO accountDAO;
@Test
public void findAllAccounts() {
assertThat(accountDAO.findAll().size(), is(2));
}
}
我的-dbunit-dataset.xml :
<dataset>
<account id="1" value1="foo" />
<account id="2" value1="bar" />
</dataset>
DBunit 真的很强大。你可以在这里找到官方 link :
http://dbunit.sourceforge.net/howto.htmlDbUnit - Getting started
您还可以为 table
字段定义 setter 方法:
protected void setTable(String pTableName)
this.table = pTableName;
}
并在测试 class before()
方法中调用 setter。
我正在尝试测试 Spring @Repository
组件,但首先我必须注入 table 名称才能使其正常工作。
这是我做的。这是简单的 DAO:
@Repository
@Transactional
public class AccountDAO {
@Autowired
private JdbcTemplate jdbcTemplate;
private String table = "accounts"; <-- I need to inject this value from tests
...
}
单元测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-web-servlet.xml")
@Transactional
public class AccountDaoTest {
@Autowired
private AccountDAO accountDAO;
@Before
public void init() {
accountDAO = Mockito.spy(accountDAO);
ReflectionTestUtils.setField(accountDAO, "table", "accounts_test");
}
...
}
问题是 ReflectionTestUtils
忽略了字段分配并且 accountDAO
仍然使用 accounts
table 名称值而不是 accounts_test
。
我该如何解决这个问题?
您可以使用两种技术来更改存储在私有变量中的 table 名称:
ReflectionTestUtils
没有模拟间谍。- 使用 Mockito 的注解@Spy 和@InjectMocks
但这不是非常可靠的集成测试,因为当有人更改 table
架构时,您的测试不会发现问题。有多种方法可以解决此类问题:
- 如果您的应用程序执行增量脚本以将数据库架构和元数据迁移到最新版本(例如通过使用 Liquibase 或 FlyWay),您可以重新创建数据库架构 in-memery/embedded 数据库(例如 H2、HSQL) 在测试期间和 运行 对照原始 table 进行测试。这是现代方法。
- 如果您的公司架构是老式的,一些 DBA 在您的应用程序部署中独立执行 SQL 增量脚本,则必须有一些 DEV 数据库环境。使用该环境进行集成测试。您可以整理 up/prepare 您的 table 以便预先测试。
在这里,您想要分离真实数据库和集成测试数据库。您的目标是测试 DAO 层是否运行良好。例如,我建议完全删除 Mockito 并使用 DBUnit。
然后,你保留你的 DAO,Spring 一个 JUnit。不需要注入另一个 table 名称。
DBUnit 允许您根据 XML 数据集创建仅用于测试的小型数据库。
像这样:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersistenceContext.class})
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
@DatabaseSetup("my-dbunit-dataset.xml")
public class AccountDAOTest {
@Autowired
private AccountDAO accountDAO;
@Test
public void findAllAccounts() {
assertThat(accountDAO.findAll().size(), is(2));
}
}
我的-dbunit-dataset.xml :
<dataset>
<account id="1" value1="foo" />
<account id="2" value1="bar" />
</dataset>
DBunit 真的很强大。你可以在这里找到官方 link :
http://dbunit.sourceforge.net/howto.htmlDbUnit - Getting started
您还可以为 table
字段定义 setter 方法:
protected void setTable(String pTableName)
this.table = pTableName;
}
并在测试 class before()
方法中调用 setter。