views:

4463

answers:

4

Hi guys, What is the best practice way to do unit testing with Spring? I assume the combination TestNG & jmockit with Spring 3 isn't bad, so that's what I'm doing right now, but if I'm off course selecting the tools for my fresh Spring project, please tell me right away. :-)

Anyways, I've created an entity that I want to test, but I'm not sure how to actually run the TestNG from the Spring context. I've created a simple test class

package com.mydomain.data.entities.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.*;

@ContextConfiguration(locations={"classpath:applicationContext.xml"}) 
public class SimpleTest extends AbstractTestNGSpringContextTests {

    @Autowired
    private ApplicationContext applicationContext;

    @BeforeClass
    protected void setUp() throws Exception {
     Assert.assertNotNull(applicationContext);
    }

    @Test
    public void testNothing() {

    }

}

with applicationContext.xml importing beans for setting up the model and business layer. First off all, I'd like it to use the applicationContext.xml from Project/WebRoot/WEB-INF, whereas the source for this test lives in Project/src/com/mydomain/data/entities/test, but I believe that /applicationContext.xml would give me Projects/src, same as classpath: Is this correct?

Furthermore, now that I'm using the model and business layers that my web application would use, I should expect it to behave similarly. But, when I launch TestNG on the test, it sais: class org.hibernate.cfg.ExtendedMappings has interface org.hibernate.cfg.Mappings as super class:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in URL [file:/Users/niklas/Documents/Eclipse/Project/WebRoot/WEB-INF/Project-Model.xml]: Invocation of init method failed; nested exception is java.lang.IncompatibleClassChangeError: class org.hibernate.cfg.ExtendedMappings has interface org.hibernate.cfg.Mappings as super class
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1393)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:449)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:289)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:286)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:192)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:352)
    at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:266)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:121)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.<init>(PersistenceExceptionTranslationInterceptor.java:77)
    at org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisor.<init>(PersistenceExceptionTranslationAdvisor.java:70)
    at org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor.setBeanFactory(PersistenceExceptionTranslationPostProcessor.java:99)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1414)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1381)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
    ... 38 more
Caused by: java.lang.IncompatibleClassChangeError: class org.hibernate.cfg.ExtendedMappings has interface org.hibernate.cfg.Mappings as super class
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:700)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:319)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:330)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:254)
    at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:399)
    at org.hibernate.cfg.AnnotationConfiguration.createExtendedMappings(AnnotationConfiguration.java:187)
    at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:277)
    at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1162)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:667)
    at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:211)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1452)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1390)
    ... 53 more

What is this and why am I getting this? Any clues?

My sessionFactory looks like this

  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="annotatedClasses">
      <list>
       <!-- Entity classes from package com.mydomain.data.entities -->
      </list>
    </property>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
        <prop key="hibernate.show_sql">false</prop>
      </props>
    </property>
  </bean>

Cheers

Nik

A: 

Consult the chapter on TestNG in the Spring documentation:

Grzegorz Oledzki
Don't worry, I only wrote here after reading "Spring TestContext Framework" in the Spring 3 Reference manual twice and reading quite a few tutorials
niklassaers
+1  A: 

Your test ckass needs to extend AbstractTestNGSpringContextTests. Or if your code is testing database acces another useful class to extend is AbstractTransactionalTestNGSpringContextTests.

mR_fr0g
Thanks you very much. Extending AbstractTestNGSpringContextTests made it load the context, but it gives me a strange exception while initializing. I've updated my question with the exceptions it gives to my context
niklassaers
+1  A: 

Now the problem doesn't seem to be related to any of Spring or TestNG. The key part is:

 java.lang.IncompatibleClassChangeError: class org.hibernate.cfg.ExtendedMappings has interface org.hibernate.cfg.Mappings as super class

Are you sure your CLASSPATH is OK? I.e. you have compatible version of Hibernate modules and no repetitions there?

What if you create a simple TestNG test that does the following:

Class.forName("org.hibernate.cfg.ExtendedMappings");

?

Grzegorz Oledzki
So if I give the test an empty context I can run a test that includes Class.forName, that gives me: java.lang.IncompatibleClassChangeError: class org.hibernate.cfg.ExtendedMappings has interface org.hibernate.cfg.Mappings as super class But when I run the same in my servlet context, I don't have a problem. And they have the same classpath.
niklassaers
I believe the only place ExtendedMappings is defined in any of my jar-files is in hibernate-annotations.jar.
niklassaers
Hmm, this may be because I'm running Hibernate 3.5-beta1 with Hibernate-Annotations 3.4. Let me try and downgrade to 3.4 and see if they're more aligned then :-)
niklassaers
When looking at:http://fisheye.jboss.org/browse/Hibernate/core/trunk/core/src/main/java/org/hibernate/cfg/Mappings.javayou can see that org.hibernate.cfg.Mappings is an interface in one of the versions (15365) and a class in other (11588). That confirms the suspicion of wrong Hibernate JARs being used.
Grzegorz Oledzki
Err... 3.4 doesn't exist. So I'll give Hibernate Core 3.3.2 with Hibernate Annotations 3.4 a try
niklassaers
You may find useful the Compability Matrix of Hibernate modules: https://www.hibernate.org/6.html#A3 .
Grzegorz Oledzki
Thanks a bunch. You are, of course, very correct in your suspicion. Going back to 3.3.2 aligned core and annotations again. :-)
niklassaers
A: 

You might want to try hibernate-annotations-3.5.0-Beta-1 as this aligns with hibernate-core-3.5.0-Beta-1.

I still get the java.lang.IncompatibleClassChangeError sometimes, as I'm trying to use hibernate search which is using the "old" annotations.

Also, not sure if you are running JBoss, but if you are you'll get this error with core 3.5.0.

Kango_V
If you're using hibernate-core-3.5.x, it should include the annotations (and entityManager) - separate jars no longer required according to hibernate https://www.hibernate.org/397.html
John
That's what i thought, but I still need to pull annotations in. If you look at hibernate-annotations pom it has hibernate-core as a dependency. What i think hibernate means is that the version numbers are aligned. Or have I been drinking too much beer :)
Kango_V