Spring 自动装配在正在测试的类中不起作用

发布于 2024-11-15 14:02:25 字数 5500 浏览 3 评论 0原文

我正在尝试对我正在编写的 Web 服务中使用的部分代码运行高级测试。在此过程中,代码预计会调用数据库(我使用 Spring 和 Hibernate 设置了该数据库)。自动装配在测试用例类本身中工作正常,但由于运行测试而调用的其他类没有被自动装配。

单元测试

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration
public class ProviderTest
{
@Autowired
protected ApplicationContext applicationContext;
protected ProviderA provider;

@Before
public void setUp()
{
    Transaction transaction = DbLogHelper.getInstance().getLog();
    transaction.setCompanyName("Unit Test");
    transaction.setCreateTime(Calendar.getInstance());
    transaction.setPhoneNumber("Provider Test - N/A");
    transaction.setQueryId("Unit Test");
    transaction.setRequest("<NONE - UNIT TEST>");
    transaction.setResponse("<NONE - UNIT TEST>");
    LogJob job = new LogJob(transaction, DbLogHelper.getInstance.getDao());
    job.run();

    provider = new ProviderA(transaction);
}

@After
public void tearDown()
{
    Transaction transaction = DbLogHelper.getInstance().getLog();
    transaction.setResponseTime(Calendar.getInstance());
    DbLogHelper.getInstance().writeLog(transaction);
    DbLogHelper.getInstance().resetLog();
}

@Test
public void testProvider()
{
    try
    {
        List<TAddress> results = provider.searchItem("SOME_ITEM", "12345");
        Assert.assertNotNull(results);
    }
    catch (Exception e)
    {
        Assert.fail();
    }
}
}

单例日志帮助程序

public class DbLogHelper
{
private static DbLogHelper instance;
@Autowired
private LogDao dao;
private Executor executor = new ThreadPoolExecutor(3, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
private ThreadLocal<Transaction> transactionRef = new ThreadLocal<Transaction>() {

    @Override
    protected Transaction initialValue()
    {
        return new Transaction();
    }

};

public static DbLogHelper getInstance()
{
    if (instance == null)
    {
        synchronized (DbLogHelper.class)
        {
            if (instance == null)
            {
                instance = new DbLogHelper();
            }
        }
    }

    return instance;
}

/**
 * Gets the Transaction object attached to this thread. Generates a new one if it does not
 * yet exist.
 * @return Transaction
 */
public Transaction getLog()
{
    return transactionRef.get();
}

/**
 * Removes the current Transaction object from the thread map. The next getLog() call will
 * generate a new one.
 */
public void resetLog()
{
    transactionRef.remove();
}

/**
 * Gets the DAO used to persist Transaction objects
 */
public LogDao getDao()
{
    return dao;
}

/**
 * Queues the passed Transaction object to be written to the database. This happens in a
 * separate thread so the service is not waiting on database persistence before responding.
 * @param transaction
 */
public void writeLog(Transaction transaction)
{
    LogJob job = new LogJob(transaction, dao);
    executor.execute(job);
}
}

日志 DAO

@Repository
public class LogDao
{
private static Logger log = LoggerFactory.getLogger(LogDao.class);
private EntityManager entityManager;

@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
    log.debug("Entity Manager set on LogDao");
    this.entityManager = entityManager;
}

@Transactional
public void updateTransaction(Transaction log)
{
    log = entityManager.merge(log);
}
}

ProviderTest-context.xml

<!-- database -->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <!-- uncomment to see sql
         <property name="driverClassName" value="com.p6spy.engine.spy.P6SpyDriver" />
    -->
    <property name="url" value="jdbc:mysql://localhost" />
    <property name="username" value="root" />
    <property name="password" value="" />
</bean>

<!-- LOCAL entity manager factory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <!-- <property name="persistenceUnitManager" ref="persistenceUnitManager"/> -->
    <property name="persistenceUnitName" value="domainPersistenceUnit" />
    <property name="dataSource" ref="datasource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="false" />
            <!-- <property name="generateDdl" value="true" /> -->
            <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
        </bean>
    </property>
</bean>

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="dataSource" ref="datasource" />
</bean>

<context:annotation-config/>
<context:component-scan base-package="com.company.application.webservice"/>

DbLogHelper 中的自动装配 DAO 始终以 null 结束。但是,如果我直接在单元测试中自动装配它,它就会被注入到准备好的实体管理器中。当然,我担心的是,如果它在测试中不起作用,那么在正确部署后它也不会起作用。即使我不必担心这一点,我仍然想测试一下。

I'm attempting to run high level test on a portion of code used in a web service I'm writing. During the process, the code is expected to make calls to a database (which I've set up using Spring and Hibernate). Autowiring is working fine in the test case class itself, but other classes that are being invoked as a result of the test being run are not being Autowired.

Unit Test

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration
public class ProviderTest
{
@Autowired
protected ApplicationContext applicationContext;
protected ProviderA provider;

@Before
public void setUp()
{
    Transaction transaction = DbLogHelper.getInstance().getLog();
    transaction.setCompanyName("Unit Test");
    transaction.setCreateTime(Calendar.getInstance());
    transaction.setPhoneNumber("Provider Test - N/A");
    transaction.setQueryId("Unit Test");
    transaction.setRequest("<NONE - UNIT TEST>");
    transaction.setResponse("<NONE - UNIT TEST>");
    LogJob job = new LogJob(transaction, DbLogHelper.getInstance.getDao());
    job.run();

    provider = new ProviderA(transaction);
}

@After
public void tearDown()
{
    Transaction transaction = DbLogHelper.getInstance().getLog();
    transaction.setResponseTime(Calendar.getInstance());
    DbLogHelper.getInstance().writeLog(transaction);
    DbLogHelper.getInstance().resetLog();
}

@Test
public void testProvider()
{
    try
    {
        List<TAddress> results = provider.searchItem("SOME_ITEM", "12345");
        Assert.assertNotNull(results);
    }
    catch (Exception e)
    {
        Assert.fail();
    }
}
}

Singleton Log Helper

public class DbLogHelper
{
private static DbLogHelper instance;
@Autowired
private LogDao dao;
private Executor executor = new ThreadPoolExecutor(3, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
private ThreadLocal<Transaction> transactionRef = new ThreadLocal<Transaction>() {

    @Override
    protected Transaction initialValue()
    {
        return new Transaction();
    }

};

public static DbLogHelper getInstance()
{
    if (instance == null)
    {
        synchronized (DbLogHelper.class)
        {
            if (instance == null)
            {
                instance = new DbLogHelper();
            }
        }
    }

    return instance;
}

/**
 * Gets the Transaction object attached to this thread. Generates a new one if it does not
 * yet exist.
 * @return Transaction
 */
public Transaction getLog()
{
    return transactionRef.get();
}

/**
 * Removes the current Transaction object from the thread map. The next getLog() call will
 * generate a new one.
 */
public void resetLog()
{
    transactionRef.remove();
}

/**
 * Gets the DAO used to persist Transaction objects
 */
public LogDao getDao()
{
    return dao;
}

/**
 * Queues the passed Transaction object to be written to the database. This happens in a
 * separate thread so the service is not waiting on database persistence before responding.
 * @param transaction
 */
public void writeLog(Transaction transaction)
{
    LogJob job = new LogJob(transaction, dao);
    executor.execute(job);
}
}

Log DAO

@Repository
public class LogDao
{
private static Logger log = LoggerFactory.getLogger(LogDao.class);
private EntityManager entityManager;

@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
    log.debug("Entity Manager set on LogDao");
    this.entityManager = entityManager;
}

@Transactional
public void updateTransaction(Transaction log)
{
    log = entityManager.merge(log);
}
}

ProviderTest-context.xml

<!-- database -->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <!-- uncomment to see sql
         <property name="driverClassName" value="com.p6spy.engine.spy.P6SpyDriver" />
    -->
    <property name="url" value="jdbc:mysql://localhost" />
    <property name="username" value="root" />
    <property name="password" value="" />
</bean>

<!-- LOCAL entity manager factory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <!-- <property name="persistenceUnitManager" ref="persistenceUnitManager"/> -->
    <property name="persistenceUnitName" value="domainPersistenceUnit" />
    <property name="dataSource" ref="datasource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="false" />
            <!-- <property name="generateDdl" value="true" /> -->
            <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
        </bean>
    </property>
</bean>

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="dataSource" ref="datasource" />
</bean>

<context:annotation-config/>
<context:component-scan base-package="com.company.application.webservice"/>

The autowired DAO in the DbLogHelper always ends up null. However, if I autowire it directly in the unit test, it gets injected just fine the entity manager ready to go. My fear, of course, is that if it's not working in the test, it will not work when deployed proper. Even if I need not be worried about that, I'd still like to test it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

带刺的爱情 2024-11-22 14:02:26

您应该将 DBLogger 定义为 spring bean,如 @John Vint 提到的,或者如果由于某种原因不能,您可以从 spring 应用程序上下文中获取要自动装配的对象(不推荐)。

有关后者的详细信息:
http://www.javacodegeeks .com/2015/03/using-spring-management-bean-in-non-management-object.html

You should either define DBLogger as a spring bean as mentioned by @John Vint, or If you can't due to some reason, you can get the object you want to autowire from spring application context (not recommended).

For details on the latter:
http://www.javacodegeeks.com/2015/03/using-spring-managed-bean-in-non-managed-object.html

ぽ尐不点ル 2024-11-22 14:02:25

DBLogger 不是 spring bean,因此 spring 无法将任何类自动装配到它(因为它不知道)。我建议将它定义为 spring 上下文中的 bean,并将 DbLogHelper 注入到您想要使用它的任何类中。

DBLogger is not a spring bean so spring cannot Autowire any class to it (because it doesnt know about it). I would recommend defining it as a bean in your spring context and injecting DbLogHelper into any class you want to use it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文