3

I'm attempting to unit test a class that needs a dependency. Creating the dependency class directly is not possible, as the constructor of the dependency has logic that needs objects only available at runtime.

However, when I attempt to mock the dependency class, I get a "java.lang.NoClassDefFoundError: javax/ejb/EJBLocalObject" exception. How can I go around this? The only solution I can think of at the moment is to change the ClassToTest to use an interface instead of the actual concrete Dependency class.

Some code to illustrate how I'm currently attempting to mock the dependency:

package mockTest;

import org.junit.Test;
import org.powermock.api.mockito.PowerMockito;

public class MockTest {

    @Test
    public void performTest() {
        // Mock the dependency and create the class to test
        Dependency dependency = PowerMockito.mock(Dependency.class);
        ClassToTest classToTest = new ClassToTest(dependency);

       // Invoke a method in classToTest, assert..
    }
}

Further clarification:

Q: Is the Dependent class an impl of an interface?

ClassToTest (the dependent class) is a concrete class and implements no interfaces, although it easily could - I control the source. The Dependency class is a concrete class that does not implement interfaces and I have no control over the source.

Q: Are you only passing in the "dependency" object because it gets created in the normal constructor, so by providing one, you try and privde the working Mock instead of the created one?

Yes. ClassToTest uses methods of Dependency that cause the Dependency to e.g. make JDBC calls. I want to be able to either pass in the actual Dependency (from the implementation code) or a mock (from the test code).

Q: Is the dependency a static?

No, the Dependency or the methods in the Dependency are not static.

4

2 回答 2

1

经过进一步研究,这个问题似乎与使用 Maven 构建我正在处理的项目的方式有关。

抛出 java.lang.NoClassDefFoundError 是因为包含 javax.ejb.EJBLocalObject 的 Maven 工件具有“提供”的 Maven 范围。这会导致在运行时使用 Dependency 对象在实现代码中工作。但是,当测试代码尝试模拟 Dependency 时,Maven 工件不可用并抛出 NoClassDefFoundError。

解决此问题的一种快速方法是将包含 javax.ejb.EJBLocalObject 作为具有测试范围的 Maven 依赖项添加到 POM,例如:

<!--Provides javax.ejb.EJBLocalObject to tests, enabling mocking of Dependency-->
<dependency>
  <groupId>jboss</groupId>
  <artifactId>jboss-j2ee</artifactId>
  <version>3.2.1</version>
  <scope>test</scope>
</dependency>
于 2013-08-28T04:57:48.810 回答
1

在创建模拟测试时,拥有一个需要大量依赖对象(运行时的单例静态等)的类构造函数总是很痛苦。我能想到的最好方法是使用Powermockito.whenNew()呼叫和Mockito.any(Class.class)呼叫的某种组合。这样,当您的构造函数被调用时,您可以挂钩所需的对象。如果您在依赖对象上遵循类似的单例模式,您可以模拟这些类getInstance()调用以返回您在测试中实例化的模拟的副本。

例子:

Dependency dependMock = PowerMockito.mock(Dependency.class);
PowerMockito.whenNew(Dependency.class).withNoArguments().thenReturn(dependMock);

或者,如果Dependency需要为您的 2 级依赖项传入参数:

Dependency dependMock = PowerMockito.mock(Dependency.class);
PowerMockito.whenNew(Dependency.class)
  .withArguments(Mockito.any(LevelTwoDependency.class)).thenReturn(dependMock);

请注意,在使用时,whenNew您需要为您正在测试的类设置一个钩子,以便触发代码。这可以通过为测试类添加一些模拟注释来完成。

@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassYouAreTesting.class})
public class ClassTest{
    // code    
}

这使您可以使用 Mocks 完全控制您的 2 级依赖对象。通过从逻辑中删除它们(通过提供 Mocked 功能),您可以进入测试的核心,最终测试相关类的纯功能。通过从这些依赖类中删除任何奇怪的行为,您可以消除该代码中存在错误的风险,这将最终破坏并为使用这些依赖类的任何类提供错误的测试结果。

通过将这些 Mocks 链接在一起,您基本上可以使用其他模拟来模拟主依赖类及其所有子依赖类。根据构造函数的工作方式以及您为这些对象设置私有变量的方式,它可能会变得有些混乱。

于 2013-08-16T15:03:22.590 回答