简体   繁体   English

JUnit&Mockito:如何将字段值注入Spring组件?

[英]JUnit&Mockito: how to inject field values to Spring component?

I'm trying to test Spring @Repository component but first I've to inject table name to let this work properly. 我正在尝试测试Spring @Repository组件,但首先我必须注入表名以使其正常工作。

Here's what I made. 这就是我做的 It's simple DAO: 这是简单的DAO:

@Repository
@Transactional
public class AccountDAO {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private String table = "accounts"; <-- I need to inject this value from tests
    ...
}

the unit test: 单元测试:

@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");
    }

    ...
}

The problem is that ReflectionTestUtils ignores field assignment and accountDAO still uses accounts table name value instead of accounts_test . 问题是, ReflectionTestUtils忽略字段赋值和accountDAO仍然使用accounts表名值,而不是accounts_test

How can I round this problem? 我该如何解决这个问题?

You can use two techniques to change the table name stored in private variable: 您可以使用两种技术来更改存储在私有变量中的表名:

  1. ReflectionTestUtils without mockito spying. 无需模仿间谍活动的ReflectionTestUtils
  2. Use Mockito's annotations @Spy and @InjectMocks 使用Mockito的注释@Spy和@InjectMocks

But this is not very reliable integration testing, because when somebody changes table schema, your test wouldn't catch the problem. 但这不是一个非常可靠的集成测试,因为当有人更改table架构时,您的测试将无法解决问题。 There are ways how to tackle such problem: 有一些方法可以解决此类问题:

  1. If your application executes delta-scripts to migrate DB schema and metadata to latest version (eg by using Liquibase or FlyWay), you can recreate DB schema in-memery/embedded database (eg H2, HSQL) during test and run test against against original table. 如果您的应用程序执行增量脚本以将数据库模式和元数据迁移到最新版本(例如,通过使用Liquibase或FlyWay),则可以在测试过程中重新创建数据库模式的内置/嵌入式数据库(例如H2,HSQL),并针对原始数据库进行测试表。 This is modern approach. 这是现代方法。
  2. If your company architecture is old fashioned, where some DBAs execute SQL delta scripts independently on your app deployment, there has to be some DEV DB environment. 如果您的公司体系结构是老式的,其中某些DBA在您的应用程序部署上独立执行SQL增量脚本,则必须有一些DEV DB环境。 Use that environment for your integration testing. 使用该环境进行集成测试。 You can tidy up/prepare your table for testing upfront. 您可以整理/准备表以进行前期测试。

Here, your want to separate real database and integration test database. 在这里,您要分离真实数据库和集成测试数据库。 Your goal is to test that DAO layer works well. 您的目标是测试DAO层是否运作良好。 I suggest to remove completely Mockito and use DBUnit for instance. 我建议完全删除Mockito并使用DBUnit作为实例。

Then, you keep your DAO, Spring an JUnit. 然后,保留DAO,并生成一个JUnit。 Injecting another table name is not necessary. 不需要注入另一个表名。

DBUnit allows you to create a small database only used for testing according to a XML dataset. DBUnit允许您创建一个仅用于根据XML数据集进行测试的小型数据库。

Something like this: 像这样:

@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));
    }
}

my-dbunit-dataset.xml : my-dbunit-dataset.xml:

<dataset>
    <account id="1" value1="foo" />
    <account id="2" value1="bar" />
</dataset>

DBunit is really powerfull. DBunit确实功能强大。 You can find the official link here : 您可以在此处找到官方链接:

http://dbunit.sourceforge.net/howto.html DbUnit - Getting started http://dbunit.sourceforge.net/howto.html DbUnit-入门

You can also define a setter method for table field: 您还可以为table字段定义setter方法:

protected void setTable(String pTableName) this.table = pTableName; }

and invoke the setter in the test class before() method. 并在测试类的before()方法中调用设置器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM