通常のクラスと単体テストからSpring Beanを作成する

Springを積極的に使用するリッチクライアントとサーバーの両方があります。 そして、問題が非常に迅速に発生しました-通常のクラス(それ自体はビンではありません)のスプリングビンを使用する方法。



最初に、必要なBeanをコンストラクターの引数として渡すか、何らかの静的フィールドを使用してSpringコンテキストを格納するという2つのアイデアが浮上しました。

最初のアイデアは悪質でした。 スキーサービスは、一連の長いデザイナーを通じて引き出されなければならないことがわかりました。

2番目のアイデアにも欠陥があると認識されていました-誰がこのフィールドを初期化するか、いつ、何時に単体テストが行​​われるかという疑問が生じます。



すぐに、私はインターネットでこのような美しいオプションをグーグルで検索しました:





@Service public class StaticContextHolder implements BeanFactoryAware { public static BeanFactory CONTEXT; public StaticContextHolder() { } public static Object getBean(String s) throws BeansException { return CONTEXT.getBean(s); } public static <T> T getBean(String s, Class<T> tClass) throws BeansException { return CONTEXT.getBean(s, tClass); } public static <T> T getBean(Class<T> tClass) throws BeansException { return CONTEXT.getBean(tClass); } public static Object getBean(String s, Object... objects) throws BeansException { return CONTEXT.getBean(s, objects); } public static boolean containsBean(String s) { return CONTEXT.containsBean(s); } @Override public void setBeanFactory(BeanFactory applicationContext) throws BeansException { logger.assertNull(CONTEXT, "CONTEXT is not null. Double Spring context creation?"); CONTEXT = applicationContext; } }
      
      







そしてそれは素晴らしく機能します。

ただし、単体テストの場合、これを少し変更する必要がありました。



湧き上がるコンテキストを作成するテストがあります。 したがって、このクラスにこのメソッドを追加しました。



  @PreDestroy public void resetStatics() { CONTEXT=null; }
      
      







第二に、単体テストがspring kotextを作成せず、テストされたクラスがStaticContextHolderを使用する場合、ユニットから依存関係を受け取る必要があります。



ダミーコンテキストを作成しました。

 public class FakeBeanFactory implements BeanFactory { private Map<String, Object> beans; public FakeBeanFactory (Map<String, Object> beans) { this.beans = beans; } @Override public Object getBean(String s) throws BeansException { return beans.get(s); } @Override public <T> T getBean(String s, Class<T> tClass) throws BeansException { return (T) beans.get(s); } @Override public <T> T getBean(Class<T> tClass) throws BeansException { return (T) beans.get(tClass.getName()); } @Override public Object getBean(String s, Object... objects) throws BeansException { return beans.get(s); } @Override public boolean containsBean(String s) { return false; //     } @Override public boolean isSingleton(String s) throws NoSuchBeanDefinitionException { return false; //     } @Override public boolean isPrototype(String s) throws NoSuchBeanDefinitionException { return false; //     } // .... }
      
      







単体テストの初期化は次のようになります。

  @Before public void init() { Map<String,Object> beans = new Map<String,Object>(); beans.put("service-dependency", new MockupDependencyImpl()); StaticContextHolder.CONTEXT = new FakeBeanFactory(beans)); }
      
      







もう1つの問題が残っています。プロトタイプBeanは、たとえば、initメソッドを使用して作成されます。

  <bean id="PlanDefinitionReader" class="com.example.PlanDefinitionReader" scope="prototype" factory-method="createPlanDefinitionReader"> <constructor-arg index="0" value="null"/> <constructor-arg index="1" value="null"/> <constructor-arg index="2" value="null"/> </bean>
      
      







これを行うには、FakeBeanfactoryに別のマップを追加します。

 Map<String s, Method m> initMethods
      
      







そして、1つのメソッドを書き換えます:

  public Object getBean(String s, Object... objects) throws BeansException { return initMethods.get(s).invoke(null, objects); }
      
      







この静的メソッドのマップを初期化して、単体テストを初期化します。



よくここに。 このようなもの。



これらの問題をすべて解決する最善の方法を誰かが教えてくれたら嬉しいです。




All Articles