java - 我如何模拟 java.net.URL 或以编程方式设置 spring bean 来接受构造函数参数?

标签 java spring unit-testing mocking

背景
我正在开发一个 Java 应用程序套件,通过 HTTP post/get 调用不同的子系统。在我对所有内容进行单元测试的过程中,我遇到了无法使用 Mockito 的问题。直接模拟,因为 URL 类是 final

我在哪里
由于 URL 类无法被模拟,因此我决定创建一个包装类,并且仅公开我正在使用的 URL 类的方法。您可以在下面找到该类(class)。这似乎工作得很好,直到我开始尝试在测试上下文中设置我的 bean。

UrlWrapper

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class UrlWrapper {

    URL url;

    public UrlWrapper(String spec) throws MalformedURLException{
        url = new URL(spec);
    }

    public URLConnection openConnection() throws IOException{
        return url.openConnection();
    }

}

单元测试方法

@Test
public final void testToMockConstructorInjectedBean()
        throws IOException {
    GenericApplicationContext mockContext = new GenericApplicationContext();

    // Create our mock controller
    UrlWrapper mockUrl = mock(UrlWrapper.class);

    // Set the mock object in the context
    mockContext.refresh();
    mockContext.getBeanFactory().registerSingleton("url", mockUrl);
    UrlWrapper mock = null;
    mock = (UrlWrapper)mockContext.getBean("url"); // <-- Works
    mock = (UrlWrapper)mockContext.getBean("url", "http://google.com/"); // <-- Fails.
}

问题
我真正喜欢的是弄清楚如何让单元测试中“失败”的行正常工作。这对其余代码的影响最小,并且是理想的。我可以采取的另一个选择是修改我的包装器,使其具有接受 String 并在内部创建一个新的 URL 对象的 set 方法,但对我来说感觉像是代码味道。使用 URL 进行单元测试代码的“正常”正确方法是什么?另外,如何模拟在注入(inject)期间接受构造函数参数的单元测试上下文?

编辑 1


上下文提供者

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ContextProvider {
    private static ApplicationContext appContext;

    public static void setContext(ApplicationContext context) {
        appContext = context;
    }

    public static ApplicationContext getContext() {
        if (appContext == null) {
            appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        }

        return appContext;
    }
}

单元测试之外的示例用法

public boolean post() throws IOException {
    UrlWrapper url;

    // Get my URL from the available context (either 'live' or 'test' context)
    url = (UrlWrapper) ContextProvider.getContext().getBean("url", requestUrl);

    /* More code here omitted for brevity */
}

最佳答案

对于您的情况,我建议您看一下 Mockito 的 SpyHere是 javadoc 和 here是它的使用教程。

在您的情况下,您将创建真实 URLWrapper 对象的 spy ,然后执行所需的任何其他交互。

@Test
public final void testToMockConstructorInjectedBean()
        throws IOException {
    GenericApplicationContext mockContext = new GenericApplicationContext();

    // Create our mock controller
    UrlWrapper spyUrl = spy(new URLWrapper("http://google.com"));

    // Set the mock object in the context
    mockContext.refresh();
    mockContext.getBeanFactory().registerSingleton("url", spyUrl);
    UrlWrapper spy = null;
    spy = (UrlWrapper)mockContext.getBean("url");
}

在本例中,您使用指定的 URL 创建 spy ,然后将该对象注册为该类的 bean。

更新

我认为

url = (UrlWrapper) ContextProvider.getContext().getBean("url", requestUrl);

在测试代码中是限制性的。像这样的代码意味着需要以特定方式(静态工厂方法)创建 bean,以便能够按照测试的预期进行创建。我建议您使用已包含所需行为的 bean 填充 Spring 上下文,然后仅使用不带参数的 getBean

关于java - 我如何模拟 java.net.URL 或以编程方式设置 spring bean 来接受构造函数参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25308524/

相关文章:

Java 正则表达式 : Replacing a phone number (including optional parentheses)

java - 如何使用方法测试java类

java - 使用正则表达式从Java中的txt文件中获取引号之间的字符串

Java lambda - 修改 forEach 循环中的值

ios - 如何在多个 OCUnit 测试用例之间共享代码?

spring - 使用自定义 Spring 安全注解

java - @JsonFilter,自定义和性能

java - JAAS登录模块中的用户主体-额外的属性是单独的主体还是Bean属性?

c# - 从同一解决方案中另一个项目的 View 调用类库中的函数

ios - Xcode:单元测试时 URL 共享 session 未运行