You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

CloudStack 4.1 has switched over to using Spring for component injection and aop.  This makes it much more flexible in terms of working with Junit.  This page details how to write a test.  This assumes you're already familiar with JUnit.

Test

If the JUnit test you created is testing a component that requires component injection, you should add the following to the top of the class.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)

Within the class, you should add this method.  This call makes sure all of the components injected went through the proper life cycle initialization before the test happens.

    @Before
public void setup() throws Exception {
    ComponentContext.initComponentsLifeCycle();
}

Test Configuration Xml

You can pretty much copy and paste the following xml into your test configuration xml.  We're still looking for ways to get rid of the xml file completely.  Change the [Test Configuration] to your own test configuration.

<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
  license agreements. See the NOTICE file distributed with this work for additional
  information regarding copyright ownership. The ASF licenses this file to
  you under the Apache License, Version 2.0 (the "License"); you may not use
  this file except in compliance with the License. You may obtain a copy of
  the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
  by applicable law or agreed to in writing, software distributed under the
  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
  OF ANY KIND, either express or implied. See the License for the specific
  language governing permissions and limitations under the License. -->
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                      http://www.springframework.org/schema/tx
                      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                      http://www.springframework.org/schema/aop
                      http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
                      http://www.springframework.org/schema/context
                      http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:annotation-config />

  <!-- @DB support -->
  <aop:config proxy-target-class="true">
    <aop:aspect id="dbContextBuilder" ref="transactionContextBuilder">
      <aop:pointcut id="captureAnyMethod" expression="execution(* *(..))" />

      <aop:around pointcut-ref="captureAnyMethod" method="AroundAnyMethod" />
    </aop:aspect>

  </aop:config>

  <bean id="transactionContextBuilder" class="com.cloud.utils.db.TransactionContextBuilder" />
  <bean id="componentContext" class="com.cloud.utils.component.ComponentContext"/>
  <bean id="TestConfiguration"
    class="[Test Configuration]" />
  <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
    <property name="requiredParameterValue" value="false" />
  </bean>
</beans>

Test Configuration

In the XML above, you specified the class where the test configuration will be.  Here's what that test configuration looks like.  In this class, any components that's not mocked is specified in the ComponentScan annotation.  Any component's that's mocked is specified as a method with the Bean annotation.  Now, you might noticed that this class contains a Library class, which is not standard Spring.  The ComponentScan annotation automatically scans in the packages not just the class.  By using the Library filter, we are limiting the components loaded to only the classes listed in the ComponentScan annotation.  This reduces the number of components loaded.  If you do want it to scan the package, then don't add the includeFilters attribute to the annotation.

@Configuration
@ComponentScan(basePackageClasses={
        SecurityGroupRulesDaoImpl.class,
        UserVmDaoImpl.class,
        AccountDaoImpl.class,
        ConfigurationDaoImpl.class,
        SecurityGroupWorkDaoImpl.class,
        VmRulesetLogDaoImpl.class,
        VMInstanceDaoImpl.class,
        DomainDaoImpl.class,
        UsageEventDaoImpl.class,
        ResourceTagsDaoImpl.class,
        HostDaoImpl.class,
        HostDetailsDaoImpl.class,
        HostTagsDaoImpl.class,
        ClusterDaoImpl.class,
        HostPodDaoImpl.class,
        DataCenterDaoImpl.class,
        DataCenterIpAddressDaoImpl.class,
        HostTransferMapDaoImpl.class,
        SecurityGroupManagerImpl2.class,
        SecurityGroupDaoImpl.class,
        SecurityGroupVMMapDaoImpl.class,
        UserVmDetailsDaoImpl.class,
        DataCenterIpAddressDaoImpl.class,
        DataCenterLinkLocalIpAddressDaoImpl.class,
        DataCenterVnetDaoImpl.class,
        PodVlanDaoImpl.class,
        DcDetailsDaoImpl.class,
        SecurityGroupRuleDaoImpl.class,
        NicDaoImpl.class,
        SecurityGroupJoinDaoImpl.class},
        includeFilters={@Filter(value=SecurityGroupManagerTestConfiguration.Library.class, type=FilterType.CUSTOM)},
        useDefaultFilters=false
        )
public class SecurityGroupManagerTestConfiguration {

    @Bean
    public NetworkModel networkModel() {
        return Mockito.mock(NetworkModel.class);
    }

    @Bean
    public AgentManager agentManager() {
        return Mockito.mock(AgentManager.class);
    }

    @Bean
    public VirtualMachineManager virtualMachineManager(){
        return Mockito.mock(VirtualMachineManager.class);
    }

    @Bean
    public UserVmManager userVmManager() {
        return Mockito.mock(UserVmManager.class);
    }

    @Bean
    public NetworkManager networkManager(){
        return Mockito.mock(NetworkManager.class);
    }

    @Bean
    public AccountManager accountManager() {
        return Mockito.mock(AccountManager.class);
    }

    @Bean
    public DomainManager domainManager() {
        return Mockito.mock(DomainManager.class);
    }

    @Bean
    public ProjectManager projectManager() {
        return Mockito.mock(ProjectManager.class);
    }

    public static class Library implements TypeFilter {

        @Override
        public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException {
            mdr.getClassMetadata().getClassName();
            ComponentScan cs = SecurityGroupManagerTestConfiguration.class.getAnnotation(ComponentScan.class);
            return SpringComponentScanUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs);
        }

    }
}

You might also wonder why we prefer to write a java file instead of putting it all in the XML.  The problem with XML is refactoring wreaks havoc in the XML based libraries.  It's better to specify the Java file and let an editor like Eclipse take care of the refactoring propagation.  We're looking to completely eliminate the XML if possible to reduce the number of files to specify for each test.

  • No labels