如何确保没有调用实例的方法?

时间:2013-02-28 14:17:04

标签: java unit-testing mocking

我的代码中有几个small dieties,在测试时会造成麻烦。假设将小神分成碎片对于这项练习来说太费力了。

通常的问题是我想测试x()的方法Foo,但要创建Foo的实例,我需要定义N(1 @Autowire。在制作中,我必须确保这些字段是有线的,因此不能选择这些字段。

我看到可能的解决方案:

  1. 告诉Spring某些@Autowire字段在测试期间是可选的
  2. 传递不可赎回的模拟,以获取被测代码不会/不需要的任何内容。
  3. 由于我不知道如何做#1,我认为#2是要走的路。我更喜欢的是一个bean工厂,它返回一个代理,为我没有定义的任何bean的任何方法调用抛出一个异常。

    因此,对于任何未知的bean,它应该调用此create方法:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class MustNotCallMe {
    
        @SuppressWarnings( "unchecked" )
        public static <T> T create( final Class<T> type, final Class<?>... types ) {
            InvocationHandler handler = new InvocationHandler() {
    
                @Override
                public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {
    
                    if( "equals".equals( method.getName() ) && Object.class.equals( method.getDeclaringClass() ) ) {
                        return proxy == args[0];
                    }
    
                    throw new UnsupportedOperationException( "You must not call " + method );
                }
            };
    
            Class<?>[] allClasses = new Class<?>[ types.length + 1 ];
            allClasses[0] = type;
            System.arraycopy( types, 0, allClasses, 1, types.length );
    
            return (T) Proxy.newProxyInstance( MustNotCallMe.class.getClassLoader(), allClasses, handler );
        }
    }
    

    这样的事情存在吗?如果没有,我将如何在Spring 3单元测试中注入我自己的bean工厂?

    编辑我知道这个想法会扰乱任何语言纯粹主义者。只有现实很少是纯粹的。如果有人愿意加强并为我们提供重构软件所需的资金和手,我们很乐意听到它。在此之前,解决问题而无需太多手工工作的解决方案可以更好地解决我们的具体问题: - )

    尽管如此,我所需要的只是一种创建BeanFactory的方法,它永远不会抛出NoSuchBeanException,但会返回“不要叫我”代理。

2 个答案:

答案 0 :(得分:1)

我是该领域的新手,但我认为Spring的整个想法是,很容易换掉用于测试的部件和环境中的其他变化。

从一个(认为他)理解概念但没有实现任何主要内容的人的观点来看,这有什么不对:每个自动装配的部分都是一个界面。想象一个实现该接口的类,其中每个方法只调用某种错误报告机制 - 它可以是所有这些的相同的一个。创建这样一个类(手动)应该快速,而不是容易出错,现在你有'模拟'。如果你有10个这样的话,那么为它们每个人创建这样一个课不应该花那么长的时间。

现在创建一个弹簧配置文件,该文件传入这些模拟中的一些,这是给定测试集所必需的。如果某个东西调用了一个mock类,它就会吐出上面的错误。当然,不同的配置可以包括不同的模拟,用于不同的测试环境。

我在这里缺少什么?

答案 1 :(得分:1)

黄金路径是:不要在测试中使用Spring,只需使用普通的java代码设置依赖项。如果这很痛苦,那么您就有了一个强有力的指标来重构您的代码。这是一件好事。对于不应被调用的依赖项,您可以提供在每次方法调用时爆炸的模拟。实现这一目标的简单方法应该是这个漂亮的Mockito功能:http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#14

肮脏的道路:如果你陷入遗留的沼泽中,你无法忍受在测试中设置所有的依赖关系(并且存在小神就是一个指标),你可以使用{{1} } + Spring配置文件(如果你使用Spring 3.1或更高版本我认为)。

有了这个,您可以拥有一个SpringJUnit4ClassRunner配置文件,用于您的生产环境,以及一个prod配置文件,它替换所有必须不用模拟调用的bean。如上所述。 Google“spring + profiles”用于各种教程。它很容易,就像一个魅力......但只有你想要替换一组固定的bean才适合。如果你有50个测试,每个测试都需要20个bean的不同子集,而这些测试需要更换,这会更糟糕:

地狱般的道路:使用Spring,但是在每个测试级别上使用blow-up-on-call-beans替换一组特定的bean。您可以通过使用包含要替换的单个bean的配置的特殊上下文配置来实现此目的。它必须与生产bean具有相同的名称/ ID,但是在通话实施时会爆炸(如果你愿意的话,再次通过Mockito)。然后在每个测试中使用ContextConfiguration Annotation,如下所示:

integrationtest

这会使@ContextConfiguration(locations = {"applicationContext.xml", "blowUpDemiGod42.xml", "blowUpDemiGod23.xml"}) 文件中的配置覆盖blowUpDemiGod*.xml

中的配置

可能有一种地狱般的路径,你可以通过Java代码在测试中提供bean ...不确定。