

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Testabilità e inserimento delle dipendenze
<a name="test"></a>

**Topics**
+ [Integrazione di Spring](#test.spring)
+ [JUnit Integrazione](#test.junit)

Il framework è progettato per essere compatibile con l'Inversione del controllo (Inversion of Control, IoC). Le implementazioni di flussi di lavoro e di attività, nonché i lavoratori e gli oggetti di contesto forniti dal framework, si possono configurare e creare come istanze tramite contenitori come Spring. Il framework offre un'integrazione immediata con Spring Framework. Inoltre, JUnit è stata fornita l'integrazione con per le implementazioni del flusso di lavoro e delle attività di unit testing. 

## Integrazione di Spring
<a name="test.spring"></a>

Il pacchetto com.amazonaws.services.simpleworkflow.flow.spring contiene classi che semplificano l'utilizzo di Spring framework nelle applicazioni. Comprendono lavoratori di flusso di lavoro e di attività compatibili con Scope e Spring: `WorkflowScope`, `SpringWorkflowWorker` e `SpringActivityWorker`. Queste classi ti permettono di configurare le implementazioni di attività e flusso di lavoro, nonché i lavoratori interamente tramite Spring.

### WorkflowScope
<a name="test.workflowscope"></a>

`WorkflowScope` è una implementazione in ambito Spring personalizzata fornita dal framework. Lo scope ti permette di creare oggetti nel contenitore Spring la cui durata è limitata a quella di un task di decisione. I bean nello scope sono creati come istanze ogni volta che un lavoratore riceve un task di decisione. Devi utilizzare questo scope per i bean di implementazione del flusso di lavoro e per ogni altro bean da cui dipende. Per i bean di implementazione del flusso di lavoro non si devono usare gli scopes singleton e prototype forniti da Spring, perché il framework richiede la creazione di un nuovo bean per ciascun task di decisione. In caso contrario si verifica un comportamento inatteso.

Il seguente esempio mostra un frammento di codice della configurazione Spring che registra il `WorkflowScope` e poi lo utilizza per configurare un bean di implementazione del flusso di lavoro e un bean di client dell'attività.

```
<!-- register AWS Flow Framework for Java WorkflowScope -->
   <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
      <property name="scopes">
       <map>
         <entry key="workflow">
          <bean class="com.amazonaws.services.simpleworkflow.flow.spring.WorkflowScope" />
         </entry>
       </map>
      </property>
   </bean>

   <!-- activities client -->
   <bean id="activitiesClient" class="aws.flow.sample.MyActivitiesClientImpl" scope="workflow">
   </bean>

   <!-- workflow implementation -->
   <bean id="workflowImpl" class="aws.flow.sample.MyWorkflowImpl" scope="workflow">
      <property name="client" ref="activitiesClient"/>
      <aop:scoped-proxy proxy-target-class="false" />
   </bean>
```

La riga di configurazione: `<aop:scoped-proxy proxy-target-class="false" />`, utilizzata nella configurazione del bean `workflowImpl`, è obbligatoria perché `WorkflowScope` non supporta il proxy tramite CGLIB. Devi utilizzare questa configurazione per tutti i bean in `WorkflowScope` collegati a un altro bean in uno scope diverso. In questo caso, il bean `workflowImpl` deve essere collegato a un bean del lavoratore di flusso di lavoro in scope singleton (vedi l'esempio completo in basso).

Puoi approfondire l'utilizzo degli scope personalizzati nella documentazione di Spring Framework.

### Lavoratori compatibili con Spring
<a name="test.springworkers"></a>

Quando usi Spring, devi utilizzare le classi di lavoratori compatibili con Spring fornite dal framework: `SpringWorkflowWorker` e `SpringActivityWorker`. Questi lavoratori possono essere inseriti in un'applicazione tramite Spring, come illustrato nel prossimo esempio. I lavoratori compatibili con Spring implementano l'interfaccia `SmartLifecycle` di Spring e per impostazione predefinita iniziano automaticamente a eseguire il polling dei task quando viene avviato il contesto Spring. Puoi disattivare questa funzionalità impostando la proprietà `disableAutoStartup` del lavoratore su `true`.

L'esempio seguente mostra come configurare un decisore. Questo esempio utilizza le interfacce `MyActivities` e `MyWorkflow` (non mostrate qui) e le relative implementazioni, `MyActivitiesImpl` e `MyWorkflowImpl`. Le interfacce client e le implementazioni generate sono `MyWorkflowClient`/`MyWorkflowClientImpl` e `MyActivitiesClient`/`MyActivitiesClientImpl` (anch'esse non mostrate qui).

Il client delle attività viene introdotto nell'implementazione del flusso di lavoro utilizzando la funzionalità di collegamento automatico di Spring:

```
public class MyWorkflowImpl implements MyWorkflow {
   @Autowired
   public MyActivitiesClient client;

   @Override
   public void start() {
      client.activity1();
   }
}
```

La configurazione Spring per il decisore è la seguente:

```
<?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:aop="http://www.springframework.org/schema/aop"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
   http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <!-- register custom workflow scope -->
   <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
      <property name="scopes">
       <map>
         <entry key="workflow">
          <bean class="com.amazonaws.services.simpleworkflow.flow.spring.WorkflowScope" />
         </entry>
       </map>
      </property>
   </bean>
   <context:annotation-config/>

   <bean id="accesskeys" class="com.amazonaws.auth.BasicAWSCredentials">
      <constructor-arg value="{AWS.Access.ID}"/>
      <constructor-arg value="{AWS.Secret.Key}"/>
   </bean>

   <bean id="clientConfiguration" class="com.amazonaws.ClientConfiguration">
      <property name="socketTimeout" value="70000" />
   </bean>

   <!-- Amazon SWF client -->
   <bean id="swfClient"
      class="com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClient">
      <constructor-arg ref="accesskeys" />
      <constructor-arg ref="clientConfiguration" />
      <property name="endpoint" value="{service.url}" />
   </bean>

   <!-- activities client -->
   <bean id="activitiesClient" class="aws.flow.sample.MyActivitiesClientImpl" scope="workflow">
   </bean>

   <!-- workflow implementation -->
   <bean id="workflowImpl" class="aws.flow.sample.MyWorkflowImpl" scope="workflow">
      <property name="client" ref="activitiesClient"/>
      <aop:scoped-proxy proxy-target-class="false" />
   </bean>

   <!-- workflow worker -->
   <bean id="workflowWorker"
      class="com.amazonaws.services.simpleworkflow.flow.spring.SpringWorkflowWorker">
      <constructor-arg ref="swfClient" />
      <constructor-arg value="domain1" />
      <constructor-arg value="tasklist1" />
      <property name="registerDomain" value="true" />
      <property name="domainRetentionPeriodInDays" value="1" />
      <property name="workflowImplementations">
         <list>
            <ref bean="workflowImpl" />
         </list>
      </property>
   </bean>
</beans>
```

Poiché `SpringWorkflowWorker` è completamente configurato in Spring e avvia automaticamente il polling quando il contesto Spring viene inizializzato, il processo host per il decisore è semplice:

```
public class WorkflowHost {
   public static void main(String[] args){
      ApplicationContext context
          = new FileSystemXmlApplicationContext("resources/spring/WorkflowHostBean.xml");
      System.out.println("Workflow worker started");
   }
}
```

Analogamente, il lavoratore di attività può essere configurato nel modo seguente:

```
<?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:aop="http://www.springframework.org/schema/aop"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
   http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <!-- register custom scope -->
   <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
      <property name="scopes">
         <map>
            <entry key="workflow">
               <bean
                  class="com.amazonaws.services.simpleworkflow.flow.spring.WorkflowScope" />
            </entry>
         </map>
      </property>
   </bean>

   <bean id="accesskeys" class="com.amazonaws.auth.BasicAWSCredentials">
      <constructor-arg value="{AWS.Access.ID}"/>
      <constructor-arg value="{AWS.Secret.Key}"/>
   </bean>

   <bean id="clientConfiguration" class="com.amazonaws.ClientConfiguration">
      <property name="socketTimeout" value="70000" />
   </bean>

   <!-- Amazon SWF client -->
   <bean id="swfClient"
      class="com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClient">
      <constructor-arg ref="accesskeys" />
      <constructor-arg ref="clientConfiguration" />
      <property name="endpoint" value="{service.url}" />
   </bean>

   <!-- activities impl -->
   <bean name="activitiesImpl" class="asadj.spring.test.MyActivitiesImpl">
   </bean>

   <!-- activity worker -->
   <bean id="activityWorker"
      class="com.amazonaws.services.simpleworkflow.flow.spring.SpringActivityWorker">
      <constructor-arg ref="swfClient" />
      <constructor-arg value="domain1" />
      <constructor-arg value="tasklist1" />
      <property name="registerDomain" value="true" />
      <property name="domainRetentionPeriodInDays" value="1" />
      <property name="activitiesImplementations">
         <list>
            <ref bean="activitiesImpl" />
         </list>
      </property>
   </bean>
</beans>
```

Il processo di hosting del lavoratore di attività è simile a quello del decisore:

```
public class ActivityHost {
   public static void main(String[] args) {
      ApplicationContext context = new FileSystemXmlApplicationContext(
      "resources/spring/ActivityHostBean.xml");
      System.out.println("Activity worker started");
   }
}
```

### Contesto di decisione dell'introduzione
<a name="test.injectdecision"></a>

Se l'implementazione del flusso di lavoro dipende dagli oggetti del contesto, puoi introdurli facilmente utilizzando Spring come nel caso precedente. Il framework registra automaticamente i bean relativi al contesto nel contenitore Spring. Ad esempio, nel frammento di codice seguente, i diversi oggetti del contesto sono stati collegati automaticamente. Non è richiesta nessun'altra configurazione Spring degli oggetti del contesto.

```
public class MyWorkflowImpl implements MyWorkflow {
   @Autowired
   public MyActivitiesClient client;
   @Autowired
   public WorkflowClock clock;
   @Autowired
   public DecisionContext dcContext;
   @Autowired
   public GenericActivityClient activityClient;
   @Autowired
   public GenericWorkflowClient workflowClient;
   @Autowired
   public WorkflowContext wfContext;
   @Override
   public void start() {
      client.activity1();
   }
}
```

Se vuoi configurare gli oggetti del contesto nell'implementazione del flusso di lavoro tramite la configurazione Spring XML, utilizza i nomi di bean dichiarati nella classe `WorkflowScopeBeanNames` del pacchetto com.amazonaws.services.simpleworkflow.flow.spring. Per esempio:

```
<!-- workflow implementation -->
<bean id="workflowImpl" class="asadj.spring.test.MyWorkflowImpl" scope="workflow">
   <property name="client" ref="activitiesClient"/>
   <property name="clock" ref="workflowClock"/>
   <property name="activityClient" ref="genericActivityClient"/>
   <property name="dcContext" ref="decisionContext"/>
   <property name="workflowClient" ref="genericWorkflowClient"/>
   <property name="wfContext" ref="workflowContext"/>
   <aop:scoped-proxy proxy-target-class="false" />
</bean>
```

In alternativa, puoi introdurre un `DecisionContextProvider` nel bean di implementazione del flusso di lavoro e utilizzarlo per creare il contesto. Può essere utile se vuoi fornire implementazioni personalizzate del provider e del contesto.

### Introdurre le risorse nelle attività
<a name="test.injectresource"></a>

Puoi creare come istanze e configurare implementazioni di attività utilizzando un contenitore di inversione di controllo (Inversion of Control, IoC) e introdurre facilmente risorse, come le connessioni di database, dichiarandole come proprietà della classe di implementazione delle attività. Queste risorse verranno in genere assegnate come singleton. Ricorda che le implementazioni di attività sono chiamate dal lavoratore su più thread. Di conseguenza, l'accesso alle risorse condivise deve essere sincronizzato.

## JUnit Integrazione
<a name="test.junit"></a>

Il framework fornisce JUnit estensioni e implementazioni di test degli oggetti di contesto, come un orologio di test, che è possibile utilizzare per scrivere ed eseguire test unitari. JUnit Con queste estensioni, puoi testare localmente e inline l'implementazione del flusso di lavoro.

### Scrivere un semplice unit test
<a name="test.junit.simple"></a>

 Per scrivere test per il flusso di lavoro, utilizza la classe `WorkflowTest` nel pacchetto com.amazonaws.services.simpleworkflow.flow.junit. Questa classe è un' JUnit `MethodRule`implementazione specifica del framework ed esegue il codice del flusso di lavoro localmente, chiamando le attività in linea anziché tramite Amazon SWF. Questo ti dà la flessibilità per eseguire i test con la frequenza che preferisci senza alcun addebito. 

Per utilizzare questa classe, dichiara semplicemente un campo di tipo `WorkflowTest` e arricchiscilo con l'annotazione `@Rule`. Prima di eseguire i test, crea un nuovo oggetto `WorkflowTest` e aggiungi ad esso le implementazioni di attività e del flusso di lavoro. Puoi utilizzare la client factory del flusso di lavoro generata per creare un client e avviare un'esecuzione del flusso di lavoro. Il framework fornisce anche un JUnit runner personalizzato, `FlowBlockJUnit4ClassRunner` da utilizzare per i test del flusso di lavoro. Per esempio: 

```
@RunWith(FlowBlockJUnit4ClassRunner.class)
public class BookingWorkflowTest {

    @Rule
    public WorkflowTest workflowTest = new WorkflowTest();

    List<String> trace;

    private BookingWorkflowClientFactory workflowFactory
         = new BookingWorkflowClientFactoryImpl();

    @Before
    public void setUp() throws Exception {
        trace = new ArrayList<String>();
        // Register activity implementation to be used during test run
        BookingActivities activities = new BookingActivitiesImpl(trace);
        workflowTest.addActivitiesImplementation(activities);
        workflowTest.addWorkflowImplementationType(BookingWorkflowImpl.class);
    }

    @After
    public void tearDown() throws Exception {
        trace = null;
    }

    @Test
    public void testReserveBoth() {
        BookingWorkflowClient workflow = workflowFactory.getClient();
        Promise<Void> booked = workflow.makeBooking(123, 345, true, true);
        List<String> expected = new ArrayList<String>();
        expected.add("reserveCar-123");
        expected.add("reserveAirline-123");
        expected.add("sendConfirmation-345");
        AsyncAssert.assertEqualsWaitFor("invalid booking", expected, trace, booked);
    }
}
```

Puoi anche specificare un elenco separato di task per ciascuna implementazione di attività aggiunta a `WorkflowTest`. Ad esempio, se hai un'implementazione del flusso di lavoro che pianifica attività in elenchi di task specifici dell'host, puoi registrare l'attività nell'elenco di task di ciascun host:

```
for (int i = 0; i < 10; i++) {
    String hostname = "host" + i;
    workflowTest.addActivitiesImplementation(hostname,
                                             new ImageProcessingActivities(hostname));
}
```

Tieni presente che il codice in `@Test` è asincrono. Devi quindi utilizzare il client di flusso di lavoro asincrono per avviare un'esecuzione. Per verificare i risultati dei test, viene anche fornita una classe di aiuto `AsyncAssert`. Questa classe ti permette di attendere che le promesse siano pronte prima di verificare i risultati. In questo esempio, attendiamo che sia pronto il risultato dell'esecuzione del flusso di lavoro prima di verificare l'output del test.

Se utilizzi Spring, si può usare la classe `SpringWorkflowTest` invece di quella `WorkflowTest`. `SpringWorkflowTest` fornisce proprietà che puoi utilizzare per configurare facilmente le implementazioni di attività e di flusso di lavoro tramite la configurazione di Spring. Esattamente come per i lavoratori compatibili con Spring, devi utilizzare `WorkflowScope` per configurare i bean di implementazione del flusso di lavoro. In questo modo siamo sicuri che venga creato un nuovo bean di implementazione del flusso di lavoro per ogni task di decisione. Assicurati di configurare questi bean con l'impostazione scoped-proxy proxy-target-class impostata su. `false` Consulta la sezione Integrazione di Spring per maggiori dettagli. La configurazione Spring di esempio mostrata nella sezione Integrazione di Spring può essere modificata per testare il flusso di lavoro utilizzando `SpringWorkflowTest`: 

```
<?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:aop="http://www.springframework.org/schema/aop"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans ht
tp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframe
work.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <!-- register custom workflow scope -->
  <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
      <map>
        <entry key="workflow">
          <bean
            class="com.amazonaws.services.simpleworkflow.flow.spring.WorkflowScope" />
        </entry>
      </map>
    </property>
  </bean>
  <context:annotation-config />
  <bean id="accesskeys" class="com.amazonaws.auth.BasicAWSCredentials">
    <constructor-arg value="{AWS.Access.ID}" />
    <constructor-arg value="{AWS.Secret.Key}" />
  </bean>
  <bean id="clientConfiguration" class="com.amazonaws.ClientConfiguration">
    <property name="socketTimeout" value="70000" />
  </bean>

  <!-- Amazon SWF client -->
  <bean id="swfClient"
    class="com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClient">
    <constructor-arg ref="accesskeys" />
    <constructor-arg ref="clientConfiguration" />
    <property name="endpoint" value="{service.url}" />
  </bean>

  <!-- activities client -->
  <bean id="activitiesClient" class="aws.flow.sample.MyActivitiesClientImpl"
    scope="workflow">
  </bean>

  <!-- workflow implementation -->
  <bean id="workflowImpl" class="aws.flow.sample.MyWorkflowImpl"
    scope="workflow">
    <property name="client" ref="activitiesClient" />
    <aop:scoped-proxy proxy-target-class="false" />
  </bean>

  <!-- WorkflowTest -->
  <bean id="workflowTest"
    class="com.amazonaws.services.simpleworkflow.flow.junit.spring.SpringWorkflowTest">
    <property name="workflowImplementations">
      <list>
        <ref bean="workflowImpl" />
      </list>
    </property>
    <property name="taskListActivitiesImplementationMap">
      <map>
        <entry>
          <key>
            <value>list1</value>
          </key>
          <ref bean="activitiesImplHost1" />
        </entry>
      </map>
    </property>
  </bean>
</beans>
```

#### Implementazioni di attività fittizie
<a name="test.junit.mockactivity"></a>

Durante i test puoi usare implementazioni di attività reali, ma se vuoi eseguire unit test solo della logica del flusso di lavoro, puoi simulare le attività. Questo avviene fornendo un'implementazione fittizia dell'interfaccia delle attività alla classe `WorkflowTest`. Per esempio: 

```
@RunWith(FlowBlockJUnit4ClassRunner.class)
public class BookingWorkflowTest {

    @Rule
    public WorkflowTest workflowTest = new WorkflowTest();

    List<String> trace;

    private BookingWorkflowClientFactory workflowFactory
         = new BookingWorkflowClientFactoryImpl();

    @Before
    public void setUp() throws Exception {
        trace = new ArrayList<String>();
        // Create and register mock activity implementation to be used during test run
        BookingActivities activities = new BookingActivities() {

            @Override
            public void sendConfirmationActivity(int customerId) {
                trace.add("sendConfirmation-" + customerId);
            }

            @Override
            public void reserveCar(int requestId) {
                trace.add("reserveCar-" + requestId);
            }

            @Override
            public void reserveAirline(int requestId) {
                trace.add("reserveAirline-" + requestId);
            }
        };
        workflowTest.addActivitiesImplementation(activities);
        workflowTest.addWorkflowImplementationType(BookingWorkflowImpl.class);
    }

    @After
    public void tearDown() throws Exception {
        trace = null;
    }

    @Test
    public void testReserveBoth() {
        BookingWorkflowClient workflow = workflowFactory.getClient();
        Promise<Void> booked = workflow.makeBooking(123, 345, true, true);
        List<String> expected = new ArrayList<String>();
        expected.add("reserveCar-123");
        expected.add("reserveAirline-123");
        expected.add("sendConfirmation-345");
        AsyncAssert.assertEqualsWaitFor("invalid booking", expected, trace, booked);
    }
}
```

In alternativa, puoi fornire un'implementazione fittizia del client delle attività e introdurla nell'implementazione del flusso di lavoro.

### Testare gli oggetti contesto
<a name="test.junit.objects"></a>

Se l'implementazione del flusso di lavoro dipende dagli oggetti del contesto del framework, ad esempio, non `DecisionContext` è necessario fare nulla di speciale per testare tali flussi di lavoro. Quando viene eseguito un test tramite `WorkflowTest`, questo introduce automaticamente oggetti contesto di test. Quando l'implementazione del flusso di lavoro accede agli oggetti di contesto, ad esempio utilizzando`DecisionContextProviderImpl`, otterrà l'implementazione di test. Puoi manipolare questi oggetti contesto di test nel codice di test (metodo `@Test`) per creare casi interessanti di test. Ad esempio, se il flusso di lavoro crea un timer, puoi attivarlo chiamando il metodo `clockAdvanceSeconds` nella classe `WorkflowTest` per muovere l'orologio in avanti. Puoi anche accelerare l'orologio per attivare i timer in anticipo rispetto al normale utilizzando la proprietà `ClockAccelerationCoefficient` su `WorkflowTest`. Ad esempio, se il flusso di lavoro crea un timer per un ora, puoi impostare `ClockAccelerationCoefficient` su 60 per attivare il timer in un minuto. Per impostazione predefinita, `ClockAccelerationCoefficient` è impostato su 1. 

Per ulteriori dettagli sui pacchetti com.amazonaws.services.simpleworkflow.flow.test e com.amazonaws.services.simpleworkflow.flow.junit, consulta la documentazione AWS SDK per Java . 