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 junit test. This assumes you're already familiar with JUnit. Please note the comments // NOTE # which indicates what has been added to this file.
// 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. package org.apache.cloudstack.affinity; import ...; @RunWith(SpringJUnit4ClassRunner.class)// NOTE #1 @ContextConfiguration(loader = AnnotationConfigContextLoader.class) // NOTE #2 public class AffinityApiUnitTest { @Inject AffinityGroupService _affinityService; @Inject AccountManager _acctMgr; @Inject AffinityGroupProcessor _processor; @Inject AffinityGroupDao _groupDao; @Inject UserVmDao _vmDao; @Inject AffinityGroupVMMapDao _affinityGroupVMMapDao; @Inject AffinityGroupDao _affinityGroupDao; @Inject EventUtils _eventUtils; @Inject AccountDao _accountDao; @Inject EventDao _eventDao; private static long domainId = 5L; @BeforeClass public static void setUp() throws ConfigurationException { } @Before public void testSetUp() { ComponentContext.initComponentsLifeCycle(); // NOTE #3 AccountVO acct = new AccountVO(200L); acct.setType(Account.ACCOUNT_TYPE_NORMAL); acct.setAccountName("user"); acct.setDomainId(domainId); UserContext.registerContext(1, acct, null, true); when(_acctMgr.finalizeOwner((Account) anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); when(_processor.getType()).thenReturn("mock"); when(_accountDao.findByIdIncludingRemoved(0L)).thenReturn(acct); AffinityGroupVO group = new AffinityGroupVO("group1", "mock", "mock group", domainId, 200L); Mockito.when(_affinityGroupDao.persist(Mockito.any(AffinityGroupVO.class))).thenReturn(group); Mockito.when(_affinityGroupDao.findById(Mockito.anyLong())).thenReturn(group); Mockito.when(_affinityGroupDao.findByAccountAndName(Mockito.anyLong(), Mockito.anyString())).thenReturn(group); Mockito.when(_affinityGroupDao.lockRow(Mockito.anyLong(), anyBoolean())).thenReturn(group); Mockito.when(_affinityGroupDao.expunge(Mockito.anyLong())).thenReturn(true); Mockito.when(_eventDao.persist(Mockito.any(EventVO.class))).thenReturn(new EventVO()); } @Test public void createAffinityGroupTest() { when(_groupDao.isNameInUse(anyLong(), anyLong(), eq("group1"))).thenReturn(false); AffinityGroup group = _affinityService.createAffinityGroup("user", domainId, "group1", "mock", "affinity group one"); assertNotNull("Affinity group 'group1' of type 'mock' failed to create ", group); } @Test(expected = InvalidParameterValueException.class) public void invalidAffinityTypeTest() { AffinityGroup group = _affinityService.createAffinityGroup("user", domainId, "group1", "invalid", "affinity group one"); } @Test(expected = InvalidParameterValueException.class) public void uniqueAffinityNameTest() { when(_groupDao.isNameInUse(anyLong(), anyLong(), eq("group1"))).thenReturn(true); AffinityGroup group2 = _affinityService.createAffinityGroup("user", domainId, "group1", "mock", "affinity group two"); } @Test(expected = InvalidParameterValueException.class) public void deleteAffinityGroupInvalidIdTest() throws ResourceInUseException { when(_groupDao.findById(20L)).thenReturn(null); _affinityService.deleteAffinityGroup(20L, "user", domainId, "group1"); } @Test(expected = InvalidParameterValueException.class) public void deleteAffinityGroupInvalidIdName() throws ResourceInUseException { when(_groupDao.findByAccountAndName(200L, "group1")).thenReturn(null); _affinityService.deleteAffinityGroup(null, "user", domainId, "group1"); } @Test(expected = InvalidParameterValueException.class) public void deleteAffinityGroupNullIdName() throws ResourceInUseException { _affinityService.deleteAffinityGroup(null, "user", domainId, null); } @Test(expected = ResourceInUseException.class) public void deleteAffinityGroupInUse() throws ResourceInUseException { List<AffinityGroupVMMapVO> affinityGroupVmMap = new ArrayList<AffinityGroupVMMapVO>(); AffinityGroupVMMapVO mapVO = new AffinityGroupVMMapVO(20L, 10L); affinityGroupVmMap.add(mapVO); when(_affinityGroupVMMapDao.listByAffinityGroup(20L)).thenReturn(affinityGroupVmMap); AffinityGroupVO groupVO = new AffinityGroupVO(); when(_groupDao.findById(20L)).thenReturn(groupVO); when(_groupDao.lockRow(20L, true)).thenReturn(groupVO); _affinityService.deleteAffinityGroup(20L, "user", domainId, null); } @Test(expected = InvalidParameterValueException.class) public void updateAffinityGroupVMRunning() throws ResourceInUseException { UserVmVO vm = new UserVmVO(10L, "test", "test", 101L, HypervisorType.Any, 21L, false, false, domainId, 200L, 5L, "", "test", 1L); vm.setState(VirtualMachine.State.Running); when(_vmDao.findById(10L)).thenReturn(vm); List<Long> affinityGroupIds = new ArrayList<Long>(); affinityGroupIds.add(20L); _affinityService.updateVMAffinityGroups(10L, affinityGroupIds); } @Configuration @ComponentScan(basePackageClasses = {AffinityGroupServiceImpl.class, EventUtils.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false) public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { // NOTE #4 @Bean public AccountDao accountDao() { return Mockito.mock(AccountDao.class); } @Bean public AccountService accountService() { return Mockito.mock(AccountService.class); } @Bean public AffinityGroupProcessor affinityGroupProcessor() { return Mockito.mock(AffinityGroupProcessor.class); } @Bean public AffinityGroupDao affinityGroupDao() { return Mockito.mock(AffinityGroupDao.class); } @Bean public AffinityGroupVMMapDao affinityGroupVMMapDao() { return Mockito.mock(AffinityGroupVMMapDao.class); } @Bean public AccountManager accountManager() { return Mockito.mock(AccountManager.class); } @Bean public EventDao eventDao() { return Mockito.mock(EventDao.class); } @Bean public UserVmDao userVMDao() { return Mockito.mock(UserVmDao.class); } public static class Library implements TypeFilter { @Override public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); } } } }
The above example demonstrates a single self-contained unit test. It not only contains the unit test but also the spring configuration necessary to inject all of the necessary components. The configuration is contained inside the Java class TestConfiguration. Let's go over each note as commented in the code.
NOTE #1: Specifying @RunWith denotes a junit test@RunWith(SpringJUnit4ClassRunner.class)
NOTE #2: ContextConfiguration tells Spring where to find the component configuration for this Junit test. AnnotationConfigContextLoader.class specifies that the test configuration exists within this class.@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
NOTE #3: You need to initComponentsLifeCycle in setup because that causes all of the components injected follows CloudStack's life cycle management.ComponentContext.initComponentsLifeCycle();
NOTE #4: TestConfiguration Class tells Spring the configuration you want to use for this Junit test. Note the following things in the class.