Seitenhistorie
...
Codeblock | ||||
---|---|---|---|---|
| ||||
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Suite" parallel="false"> <listeners> <listener class-name="de.bmw.fdm.testng.LoggingListener" /> </listeners> <test name="Test"> <packages> <package name="de.fdm.*" /> </packages> </test> <!-- Test --> </suite> <!-- Suite --> |
Bei der Definition dieser XML Datei muss man leider einige Stolperfallen beachten.
Log4j für Unit Tests konfigurien
...
Codeblock | ||
---|---|---|
| ||
log4jlog4j.appender.logfile=org.apache.log4j.RollingFileAppender log4j.appender.logfile.File=nuclos-test.log log4j.appender.logfile.MaxBackupIndex=10 log4j.appender.logfile.MaxFileSize=2GB log4j.appender.logfile.layout=org.apache.log4j.PatternLayout #log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile.layout.ConversionPattern=%d %-5p %-9t [%c{3}] - %m%n log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout #log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.stdout.layout.ConversionPattern=%d %-5p %-9t [%c{3}] - %m%n log4j.rootLogger=INFO, stdout, logfile # http://www.benmccann.com/dev-blog/sample-log4j-properties-file/ # Adjust log levels # SQL logging log4j.logger.org.nuclos.server.dblayer=DEBUG #log4j.logger.org.springframework=DEBUG |
...
Codeblock | ||||
---|---|---|---|---|
| ||||
package de.bmw.fdm.server.explorer; import static de.fdm.common.FdmCommon.BO_Datenbedarf; import static de.fdm.common.FdmCommon.BO_Datenpakettyp; import static de.fdm.common.FdmCommon.BO_Fahrzeugbaum; import static de.fdm.common.FdmCommon.BO_Knoten; import static de.fdm.common.FdmCommon.F_Datenbedarf_fahrzeugbaum; import static de.fdm.common.FdmCommon.F_Datenbedarf_link; import static de.fdm.common.FdmCommon.F_Datenbedarf_modell; import static de.fdm.common.FdmCommon.F_Datenbedarf_pfad; import static de.fdm.common.FdmCommon.F_Datenbedarf_produktlinie; import static de.fdm.common.FdmCommon.F_Datenbedarf_sachnummer; import static de.fdm.common.FdmCommon.F_Datenpakettyp_bkrelevant; import static de.fdm.common.FdmCommon.F_Datenpakettyp_knoten; import static de.fdm.common.FdmCommon.F_Fahrzeugbaum_fahrzeug; import static de.fdm.common.FdmCommon.F_Fahrzeugbaum_knotenname; import static de.fdm.common.FdmCommon.F_Fahrzeugbaum_uebergeordnknoten; import static de.fdm.common.FdmCommon.F_Knoten_bezeichnung; import static de.fdm.common.FdmCommon.F_Knoten_uebergeordnknoten; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import org.apache.log4j.Logger; import org.nuclos.common.UID; import org.nuclos.common.UsageCriteria; import org.nuclos.common.collection.Pair; import org.nuclos.common.dal.vo.EntityObjectVO; import org.nuclos.server.dal.DalSupportForGO; import org.nuclos.server.genericobject.valueobject.GenericObjectVO; import org.nuclos.server.masterdata.valueobject.MasterDataVO; import org.nuclos.server.navigation.treenode.DefaultMasterDataTreeNode; import org.nuclos.server.navigation.treenode.DefaultMasterDataTreeNodeParameters; import org.nuclos.server.navigation.treenode.GenericObjectTreeNode.RelationDirection; import org.nuclos.server.navigation.treenode.GenericObjectTreeNode.SystemRelationType; import de.fdm.common.FdmCommon; import de.fdm.common.explorer.FolderTreeNode; import de.fdm.common.explorer.PdmcaeTreeNode; class DataFactory { private static final Logger LOG = Logger.getLogger(DataFactory.class); // private final Random rand = new Random(9132278275L); DataFactory() { } EntityObjectVO<Long> finishEo(EntityObjectVO<Long> eo) { eo.setPrimaryKey(rand.nextLong()); return eo; } GenericObjectVO getBO_KnotenAsGO(GenericObjectVO parent, String name) { return DalSupportForGO.getGenericObjectVOForUnitTest(getBO_Knoten(parent, name)); } EntityObjectVO<Long> getBO_Knoten(GenericObjectVO parent, String name) { final EntityObjectVO<Long> eo = new EntityObjectVO<Long>(BO_Knoten); eo.setFieldValue(F_Knoten_bezeichnung, name + "Bez"); if (parent != null) { eo.setFieldId(F_Knoten_uebergeordnknoten, parent.getPrimaryKey()); } else { eo.setFieldId(F_Knoten_uebergeordnknoten, null); } finishEo(eo); // final MasterDataVO<Long> der = new MasterDataVO<Long>(finishEo(eo)); return eo; } MasterDataVO<Long> getBO_Datenpakettyp(GenericObjectVO parent, String name) { final EntityObjectVO<Long> eo = new EntityObjectVO<Long>(BO_Datenpakettyp); // BO_Knoten eo.setFieldValue(F_Knoten_bezeichnung, name + "Bez"); if (parent != null) { eo.setFieldId(F_Datenpakettyp_knoten, parent.getPrimaryKey()); } else { eo.setFieldId(F_Datenpakettyp_knoten, null); } // BO_Datenpakettyp eo.setFieldValue(F_Datenpakettyp_bkrelevant, true); final MasterDataVO<Long> der = new MasterDataVO<Long>(finishEo(eo)); return der; } MasterDataVO<Long> getBO_Fahrzeugbaum(MasterDataVO<Long> parent, String name) { final EntityObjectVO<Long> eo = new EntityObjectVO<Long>(BO_Fahrzeugbaum); eo.setFieldValue(F_Fahrzeugbaum_knotenname, name); eo.setFieldValue(F_Fahrzeugbaum_fahrzeug, name + "Vehicle"); if (parent != null) { eo.setFieldId(F_Fahrzeugbaum_uebergeordnknoten, parent.getPrimaryKey()); } else { eo.setFieldId(F_Fahrzeugbaum_uebergeordnknoten, null); } final MasterDataVO<Long> result = new MasterDataVO<Long>(finishEo(eo)); return result; } MasterDataVO<Long> getBO_Datenbedarf(MasterDataVO<Long> parentFahrzeugbaum, String name) { final EntityObjectVO<Long> eo = new EntityObjectVO<Long>(BO_Datenbedarf); eo.setFieldValue(F_Datenbedarf_sachnummer, name + "Sachnummer"); eo.setFieldValue(F_Datenbedarf_pfad, name + "Pfad"); eo.setFieldValue(F_Datenbedarf_modell, name + "Modell"); eo.setFieldValue(F_Datenbedarf_produktlinie, name + "PL"); eo.setFieldValue(F_Datenbedarf_link, name + "Link"); if (parentFahrzeugbaum != null) { eo.setFieldId(F_Datenbedarf_fahrzeugbaum, parentFahrzeugbaum.getPrimaryKey()); } else { eo.setFieldId(F_Datenbedarf_fahrzeugbaum, null); } final MasterDataVO<Long> result = new MasterDataVO<Long>(finishEo(eo)); return result; } PdmcaeTreeNode asPdmcaeTreeNode(MasterDataVO<Long> md) { final String name = (String) md.getFieldValue(FdmCommon.F_Fahrzeugbaum_knotenname); final String vehicle = (String) md.getFieldValue(FdmCommon.F_Fahrzeugbaum_fahrzeug); final Number id = md.getPrimaryKey(); final PdmcaeTreeNode result = new PdmcaeTreeNode(id, name, true, id, name, vehicle); return result; } DefaultMasterDataTreeNode asDefaultMasterDataTreeNode(MasterDataVO<Long> md, String name) { final Long id = md.getPrimaryKey(); final DefaultMasterDataTreeNodeParameters<Long> params = new DefaultMasterDataTreeNodeParameters<Long>(md.getEntityObject().getDalEntity(), id, null, null, name + "Label", name + "Description"); final DefaultMasterDataTreeNode<Long> result = new DefaultMasterDataTreeNode<Long>(params); return result; } FolderTreeNode asFolderTreeNode2(int level, GenericObjectVO go, List subNodes) { final FolderTreeNode result = mock(FolderTreeNode.class); when(result.getId()).thenReturn(go.getId()); when(result.getEntityUID()).thenReturn(FdmCommon.BO_Knoten); when(result.hasSubNodes()).thenReturn(subNodes != null && !subNodes.isEmpty()); when(result.getSubNodes()).thenReturn(subNodes); when(result.getLevel()).thenReturn(level); return result; } FolderTreeNode asFolderTreeNode(int level, Long id, UsageCriteria usagecriteria, String sIdentifier, Long relationId, SystemRelationType relationtype, RelationDirection direction, String sUserName, UID state, UID uidNode, Long idRoot) { if (usagecriteria == null) { usagecriteria = new UsageCriteria(null, null, null, "pseudo-mocked usagecriteria"); } final FolderTreeNode result = new FolderTreeNode(id, usagecriteria, sIdentifier, relationId, relationtype, direction, sUserName, state, uidNode, idRoot); result.setLevel(level); return result; } Pair<Date,Date> interval() { final long now = System.currentTimeMillis(); Date start = new Date(now + rand.nextInt(1234567890)); Date end = new Date(now + rand.nextInt(1234567890)); if (start.after(end)) { final Date tmp = start; start = end; end = tmp; } return new Pair<Date, Date>(start, end); } void reset() { } Random getRand() { return rand; } <S,T> List<T> list(S... ts) { final List<T> result = new ArrayList<T>(ts.length); for (S t: ts) { result.add((T) t); } return result; } <S,T> Set<T> set(S... ts) { final Set<T> result = new HashSet<T>(ts.length); for (S t: ts) { result.add((T) t); } return result; } } |
Die DataFactory
stellt die Value Objekte als Eingabe für den Test zur Verfügung. Bei Nuclos handelt es sich dabei oft um EntityObjectVOs, MasterDataVOs und/oder GenericObjectVOs. Das obrige Beispiel benutzt alle 3 Objektarten. Entscheiden für die Tests ist es, (zumindest) für jedes Value Objekt das referenziert, auch einen Primary Key zu hinterlegen. Dies gelingt besonders einfach mit einem Zufallsgenerator, dessen Startbedingung festgelegt wird. Ein solcher Generator liefert nämlich (bei jedem Test) immer wieder die gleiche (Pseudo-Zufalls-)Zahlenfolge. Nummern dieser Folge werden im Beispiel als Primary Key benutzt.
Definition Spring Context
Codeblock | ||||
---|---|---|---|---|
| ||||
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.mockito.org/spring/mockito
https://bitbucket.org/kubek2k/springockito/raw/tip/springockito/src/main/resources/spring/mockito.xsd">
<context:annotation-config />
<context:spring-configured />
<!-- No component scanning! - we place any bean needed here by hand. (tp) -->
<!-- context:component-scan base-package="org.nuclos">
<context:exclude-filter expression=".*_Roo_.*"
type="regex" />
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
</context:component-scan -->
<!-- Avoid scanning for nuclos-common (this is important for *client* performance) -->
<bean class="org.nuclos.common.ApplicationProperties"/>
<bean id="appContext" class="org.nuclos.common.SpringApplicationContextHolder" />
<bean id="domPreferencesFactory" class="org.nuclos.common.preferences.DOMPreferencesFactory" />
<!-- Services/Beans -->
<mockito:mock id="masterDataService" class="org.nuclos.server.masterdata.ejb3.MasterDataFacadeBean"/>
<mockito:mock id="genericObjectService" class="org.nuclos.server.genericobject.ejb3.GenericObjectFacadeBean" />
<mockito:mock id="parameterService" class="org.nuclos.server.common.ejb3.ParameterFacadeBean" />
<mockito:mock id="stateService" class="org.nuclos.server.statemodel.ejb3.StateFacadeBean" />
<mockito:mock id="serverMetaService" class="org.nuclos.server.servermeta.ejb3.ServerMetaFacadeBean" />
<mockito:mock id="localeService" class="org.nuclos.server.common.ejb3.LocaleFacadeBean" />
<mockito:mock id="preferencesService" class="org.nuclos.server.common.ejb3.PreferencesFacadeBean" />
<mockito:mock id="metaDataService" class="org.nuclos.server.masterdata.ejb3.MetaDataFacadeBean"/>
<mockito:mock id="resourceService" class="org.nuclos.server.resource.ejb3.ResourceFacadeBean" />
<mockito:mock id="entityService" class="org.nuclos.server.masterdata.ejb3.EntityFacadeBean" />
<mockito:mock id="entityObjectService" class="org.nuclos.server.common.ejb3.EntityObjectFacadeBean" />
<mockito:mock id="treeNodeService" class="org.nuclos.server.navigation.ejb3.TreeNodeFacadeBean" />
<mockito:mock id="lookupService" class="org.nuclos.server.common.ServerLocaleDelegate" />
<mockito:mock id="parameterProvider" class="org.nuclos.server.common.ServerParameterProvider"/>
<mockito:mock id="attributeProvider" class="org.nuclos.server.common.AttributeCache" />
<mockito:mock id="nodeProvider" class="org.nuclos.server.common.NodeCache" />
<mockito:mock id="moduleProvider" class="org.nuclos.server.genericobject.Modules" />
<mockito:mock id="stateCache" class="org.nuclos.server.common.StateCache" />
<!-- API beans -->
<bean id="clientPreferences" class="org.nuclos.common2.ClientPreferences">
<property name="preferencesFactory" ref="nuclosPreferencesSingleton" />
</bean>
<bean id="nuclosPreferencesSingleton"
class="org.nuclos.common.preferences.NuclosPreferencesSingleton">
<property name="preferencesFacadeRemote" ref="preferencesService" />
<property name="preferencesFactory" ref="domPreferencesFactory" />
</bean>
<!-- mockito:mock id="treeService" class="org.nuclos.common.metadata.TreeMetaProvider" / -->
<bean id="treeService" class="org.nuclos.common.metadata.TreeMetaProvider" />
<mockito:mock id="mdTreeNodeFactory"
class="org.nuclos.server.navigation.treenode.MasterDataTreeNodeFactory" />
<!-- generic support for *FacadeLocal proxies -->
<bean id="facadeLocalProxyFactoryBean" class="org.nuclos.server.spring.FacadeLocalProxyFactoryBean" />
<bean id="facadeLocalProxyBeanFactoryPostProcessor" class="org.nuclos.server.spring.FacadeLocalProxyBeanFactoryPostProcessor" />
<!-- entity/field meta data support -->
<bean id="xstreamSupport" class="org.nuclos.common2.XStreamSupport"/>
<bean id="metaDataProvider" class="org.nuclos.server.common.TestMetaProvider">
<constructor-arg>
<!--
this has been dump before by JMX with
org.nuclos.server.common.MetaProvider.dumpAllEntities(boolean false)
-->
<value>classpath:bmw-fdm-meta.xml</value>
</constructor-arg>
</bean>
<!-- Unavoidable dependencies of *ExplorerFacadeBeans -->
<mockito:mock id="securityCache" class="org.nuclos.server.common.SecurityCache" />
<mockito:mock id="dynamicMetaDataProcessor" class="org.nuclos.server.dal.processor.jdbc.impl.DynamicMetaDataProcessor" />
<mockito:mock id="chartMetaDataProcessor" class="org.nuclos.server.dal.processor.jdbc.impl.ChartMetaDataProcessor" />
<mockito:mock id="nucletDalProvider" class="org.nuclos.server.dal.provider.NucletDalProvider" />
<mockito:mock id="recordGrantUtils" class="org.nuclos.server.common.RecordGrantUtils" />
<mockito:mock id="nuclosUserDetailsContextHolder" class="org.nuclos.server.common.NuclosUserDetailsContextHolder" />
<mockito:mock id="nuclosRemoteContextHolder" class="org.nuclos.server.common.NuclosRemoteContextHolder" />
<mockito:mock id="masterDataFacadeHelper" class="org.nuclos.server.masterdata.ejb3.MasterDataFacadeHelper" />
<mockito:mock id="springDataBaseHelper" class="org.nuclos.server.database.SpringDataBaseHelper" />
<mockito:mock id="collectableEOEntityProvider" class="org.nuclos.common.entityobject.CollectableEOEntityProvider" />
<!-- Unavoidable local facades -->
<mockito:mock id="entityObjectFacadeLocal" class="org.nuclos.server.common.ejb3.EntityObjectFacadeLocal" />
<mockito:mock id="masterDataFacadeLocal" class="org.nuclos.server.masterdata.ejb3.MasterDataFacadeLocal" />
<mockito:mock id="genericObjectFacadeLocal" class="org.nuclos.server.genericobject.ejb3.GenericObjectFacadeLocal" />
<mockito:mock id="treeNodeFacadeLocal" class="org.nuclos.server.navigation.ejb3.TreeNodeFacadeLocal" />
<!-- Classes subject to test -->
<bean class="de.bmw.fdm.server.explorer.VehicleExplorerFacadeBean" depends-on="facadeLocalProxyBeanFactoryPostProcessor"/>
<bean class="de.bmw.fdm.server.explorer.PdmcaeExplorerFacadeBean" depends-on="facadeLocalProxyBeanFactoryPostProcessor"/>
</beans>
|
Der XML Definition für den Test Spring Context erscheint auf den ersten Blick recht kompliziert. Allerdings sind viele Teile des Test Kontext stets gleich, so dass sich der Aufwand für die Erstellung doch in Grenzen hält. Der Kontext:
- Eine große Menge unvermeidbarer Mock Objekte. Diese werden durch
mockito:mock
Tags erzeugt. - Boilerplate Code zur Konfiguration von Spring.
- Die beiden zu testenden Klassen PdmcaeExplorerFacadeBean und VehicleExplorerFacadeBean.
- Den TestMetaProvider, der die Metadaten für die BOs bereitstellt (s.o.).