简体   繁体   中英

JUnit + Spring: Dependency Injection when testing fails - CannotLoadBeanClassException

I currently have the problem that dependencies that are injected via a ClassPathXmlApplicationContext can not be found when unittesting with JUnit. To test the whole thing, i'm running Maven test. When running the application normally, everything's working fine and every dependency gets resolved. The problems start to show up as soon as this type of dependency injection is used.

Unittests of other softwaremodules that don't use this type of dependency injection also work without any problems. In my case, a module is a maven project (no maven module) that references other, lower level maven projects. (For Example, a maven project containing logic and another one, containing all relevant database models for the application that is referenced by the first one etc.)

In order to make it easier to solve the problem, i recreated it on a much smaller-scale project that i will now describe.

My smaller-scale architecture consists of three modules (Maven projects):

  • The module that contains the class that will get injected (user)
  • The module that gets tested and throws the Exception. (junittest)
  • The module that contains the Interface of the class that will be injected. Both other modules reference this one inside their respective pom.xml.

It is important to note that the second module, "junittest", does not have "user" as a direct dependency in it's pom.xml. This needs to stay that way to prevent circular dependencies.

Now the relevant classes and xml files:

Module "junittest" - the to be tested class

Whenever the method useExternalInjectedDependency() is called by a junit test, the exception (which you can see further down my post) is thrown.

public class ToBeTested {
    private final String CONTEXT_FILENAME = "tobetestedContext.xml";

    private final String USERMANAGER_BEAN_ID = "userManager";

    private IUserManager userManager;

    public void useExternalInjectedDependency() {
        if (this.userManager == null) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(CONTEXT_FILENAME);
            this.userManager = context.getBean(USERMANAGER_BEAN_ID, IUserManager.class);
            context.close();
        }
    }
}

Module "junittest" - the context file for dependency injection

<?xml version="1.0" encoding="UTF-8"?>
...
<!-- GET USERMANAGER BY DEPENDENCY INJECTION -->
<bean id="userManager" class="com.project.modules.user.UserManager">
</bean>

Module "junittest" - pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>test.test</groupId>
    <artifactId>junittest</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <main.basedir>${basedir}/../..</main.basedir>
        <maven.compiler.source>9</maven.compiler.source>
        <maven.compiler.target>9</maven.compiler.target>
        <java.version>9</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.project.modules</groupId>
            <artifactId>defaultmodels</artifactId>
            <version>[0.0.1-SNAPSHOT,)</version>
        </dependency>

        <!-- Spring dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.0.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.3.RELEASE</version>
        </dependency>

        <!-- Dependencies for testing -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- Include Maven Surefire for Unit testing -->
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.0</version>
                    <configuration>
                        <argLine>-Djdk.attach.allowAttachSelf</argLine>
                        <includes>
                            <include>**/*Test.java</include>
                        </includes>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

The error i'm getting

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running junittest.ToBeTestedTest
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.938 s <<< FAILURE! - in junittest.ToBeTestedTest
[ERROR] testDependencyInjectionViaContext(junittest.ToBeTestedTest)  Time elapsed: 0.87 s  <<< ERROR!
org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.project.modules.user.UserManager] for bean with name 'userManager' defined in class path resource [tobetestedContext.xml]; nested exception is java.lang.ClassNotFoundException: com.project.modules.user.UserManager
    at junittest.ToBeTestedTest.testDependencyInjectionViaContext(ToBeTestedTest.java:10)
Caused by: java.lang.ClassNotFoundException: com.project.modules.user.UserManager
    at junittest.ToBeTestedTest.testDependencyInjectionViaContext(ToBeTestedTest.java:10)

Is there anything i can do, to solve this problem and resolve the dependency? I'm happy to provide more information about the problem and my application if it is needed.


Update

So i kinda found a solution to my problem. Although not very clean, it does work for now. For anyone who is interested: I basically created dummy implementations for the interfaces who could not be injected correctly and put them in src/test/java. They contain no real source code aside the required implemented methods of the interface, they implement. I also created a new context.xml containing the path to the now local implementations (the dummy implementations) and put it in the src/test/resources folder.

Try this:

ApplicationContext context = new ClassPathXmlApplicationContext(CONTEXT_FILENAME);

or

ApplicationContext context = new ClassPathXmlApplicationContext("file:src/main/resources/beans.xml");

file: prefix point to file system resources, not classpath.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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