diff --git a/commands/core/drupal/batch.inc b/commands/core/drupal/batch.inc
index 2823dd6..241e61c 100644
--- a/commands/core/drupal/batch.inc
+++ b/commands/core/drupal/batch.inc
@@ -14,7 +14,7 @@
  *    The command to call to process the batch.
  *
  */
-function _drush_backend_batch_process($command = 'batch-process', $args) {
+function _drush_backend_batch_process($command = 'batch-process', $args, $options) {
   global $user;
   $batch =& batch_get();
 
@@ -31,6 +31,7 @@ function _drush_backend_batch_process($command = 'batch-process', $args) {
     // Assign an arbitrary id: don't rely on a serial column in the 'batch'
     // table, since non-progressive batches skip database storage completely.
     $batch['id'] = db_next_id();
+    $args[] = $batch['id'];
 
     $batch['progressive'] = TRUE;
 
@@ -52,12 +53,7 @@ function _drush_backend_batch_process($command = 'batch-process', $args) {
     $finished = FALSE;
 
     while (!$finished) {
-      if ($user->uid) {
-        $data = drush_invoke_process_args($command, $args, array($batch['id'], '-u', $user->uid));
-      }
-      else {
-        $data = drush_invoke_process_args($command, $args, array($batch['id']));
-      }
+      $data = drush_invoke_process_args($command, $args, $options);
       $finished = drush_get_error() || !$data || ($data['context']['drush_batch_process_finished'] == TRUE);
     }
   }
@@ -140,6 +136,8 @@ function _drush_batch_worker() {
         'finished' => &$finished,
         'message'  => &$task_message,
       );
+      // Magic wrap to catch changes to 'message' key.
+      $batch_context = new DrushBatchContext($batch_context);
       call_user_func_array($function, array_merge($args, array(&$batch_context)));
 
       if ($finished == 1) {
diff --git a/commands/core/drupal/batch_6.inc b/commands/core/drupal/batch_6.inc
index 4caba3b..ae6fb51 100644
--- a/commands/core/drupal/batch_6.inc
+++ b/commands/core/drupal/batch_6.inc
@@ -14,7 +14,7 @@
  *    The command to call to process the batch.
  *
  */
-function _drush_backend_batch_process($command = 'batch-process', $args) {
+function _drush_backend_batch_process($command = 'batch-process', $args, $options) {
   global $user;
   $batch =& batch_get();
 
@@ -28,20 +28,14 @@ function _drush_backend_batch_process($command = 'batch-process', $args) {
     // at least an empty string for the (not null) 'token' column.
     db_query("INSERT INTO {batch} (token, timestamp) VALUES ('', %d)", time());
     $batch['id'] = db_last_insert_id('batch', 'bid');
+    $args[] = $batch['id'];
 
     // Actually store the batch data and the token generated form the batch id.
     db_query("UPDATE {batch} SET token = '%s', batch = '%s' WHERE bid = %d", drupal_get_token($batch['id']), serialize($batch), $batch['id']);
 
     $finished = FALSE;
-
     while (!$finished) {
-      if ($user->uid) {
-        $data = drush_invoke_process_args($command, $args, array($batch['id'], '-u', $user->uid));
-      }
-      else {
-        $data = drush_invoke_process_args($command, $args, array($batch['id']));
-      }
-
+      $data = drush_invoke_process_args($command, $args, $options);
       $finished = drush_get_error() || !$data || (isset($data['context']['drush_batch_process_finished']) && $data['context']['drush_batch_process_finished'] == TRUE);
     }
   }
@@ -105,6 +99,8 @@ function _drush_batch_worker() {
       // Build the 'context' array, execute the function call,
       // and retrieve the user message.
       $batch_context = array('sandbox' => &$current_set['sandbox'], 'results' => &$current_set['results'], 'finished' => &$finished, 'message' => &$task_message);
+      // Magic wrap to catch changes to 'message' key.
+      $batch_context = new DrushBatchContext($batch_context);
       // Process the current operation.
       call_user_func_array($function, array_merge($args, array(&$batch_context)));
     }
diff --git a/includes/batch.inc b/includes/batch.inc
index 5260046..a59afdb 100644
--- a/includes/batch.inc
+++ b/includes/batch.inc
@@ -25,6 +25,26 @@
  *
  */
 
+/**
+ * Class extending ArrayObject to allow the batch API to perform logging when
+ * some keys of the array change.
+ *
+ * It is used to wrap batch's $context array and set log messages when values
+ * are assigned to keys 'message' or 'error_message'.
+ *
+ * @see _drush_batch_worker().
+ */
+class DrushBatchContext extends ArrayObject {
+  function offsetSet($name, $value) {
+    if ($name == 'message') {
+      drush_log($value, 'ok');
+    }
+    elseif ($name == 'error_message') {
+      drush_log($value, 'error');
+    }
+    parent::offsetSet($name, $value);
+  }
+}
 
 /**
  * Process a Drupal batch by spawning multiple Drush processes.
@@ -48,9 +68,22 @@
  *   use their own command.
  *
  */
-function drush_backend_batch_process($command = 'batch-process', $args = array()) {
+function drush_backend_batch_process($command = 'batch-process', $args = array(), $options = array()) {
+  // Command line options to pass to the command.
+  global $user;
+  if ($user->uid) {
+    $options['u'] = $user->uid;
+  }
+  // Pass through --include. It allow to provide the command to call for the
+  // backend process in a file that is not in the standard drush search paths.
+  // Needed by batchTest.php that rely on backend process commands defined in
+  // tests/unit.drush.inc.
+  if ($include = drush_get_option(array('i', 'include'), FALSE)) {
+    $options['include'] = $include;
+  }
+
   drush_include_engine('drupal', 'batch', drush_drupal_major_version());
-  _drush_backend_batch_process($command, $args);
+  _drush_backend_batch_process($command, $args, $options);
 }
 
 /**
diff --git a/tests/batchTest.php b/tests/batchTest.php
index e69de29..3112219 100644
--- a/tests/batchTest.php
+++ b/tests/batchTest.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ *   Tests the drush batch subsystem.
+ *
+ * @see includes/batch.inc
+ */
+class batchCase extends Drush_TestCase {
+
+  public function testBatch() {
+    $env = 'dev';
+    $this->setUpDrupal($env, TRUE, '7.x');
+    $root = $this->sites[$env]['root'];
+    $name = "example";
+    $options = array(
+      'root' => $root,
+      'uri' => $env,
+      'yes' => NULL,
+      'include' => dirname(__FILE__),
+    );
+    $this->drush('unit-batch', array(), $options);
+    $parsed = $this->parse($this->getOutput());
+  }
+  
+  // From backendTest.php
+  const DRUSH_BACKEND_OUTPUT_DELIMITER = 'DRUSH_BACKEND_OUTPUT_START>>>%s<<<DRUSH_BACKEND_OUTPUT_END';
+  function parse($string) {
+    $regex = sprintf(self::DRUSH_BACKEND_OUTPUT_DELIMITER, '(.*)');
+    preg_match("/$regex/s", $string, $match);
+    if ($match[1]) {
+      // we have our JSON encoded string
+      $output = $match[1];
+      // remove the match we just made and any non printing characters
+      $string = trim(str_replace(sprintf(self::DRUSH_BACKEND_OUTPUT_DELIMITER, $match[1]), '', $string));
+    }
+
+    if ($output) {
+      $data = json_decode($output, TRUE);
+      if (is_array($data)) {
+        return $data;
+      }
+    }
+    return $string;
+  }
+
+  
+}
+
diff --git a/tests/unit.drush.inc b/tests/unit.drush.inc
index 2f0fac0..10ef414 100644
--- a/tests/unit.drush.inc
+++ b/tests/unit.drush.inc
@@ -19,6 +19,11 @@ function unit_drush_command() {
     'description' => 'Return an array indicating which invoke hooks got called.',
     'bootstrap' => DRUSH_BOOTSTRAP_NONE,
   );
+  $items['unit-batch'] = array(
+    'description' => 'Run a batch process.',
+    'bootstrap' => DRUSH_BOOTSTRAP_MAX,
+  );
+
   return $items;
 }
 
@@ -73,4 +78,41 @@ function unit_invoke_log($function = NULL) {
   else {
     return $called;
   }
-}
\ No newline at end of file
+}
+
+/**
+ * Command callback.
+ */
+function drush_unit_batch() {
+  // Reduce php memory/time limits to test backend respawn.
+  // TODO.
+
+  $batch = array(
+    'operations' => array(
+       array('_drush_unit_batch_operation', array()),
+    ),
+    'finished' => '_drush_unit_batch_finished',
+    // 'file' => Doesn't work for us. Drupal 7 enforces this path
+    // to be relative to DRUPAL_ROOT.
+    // @see _batch_process().
+  );
+  batch_set($batch);
+  drush_backend_batch_process();
+
+  // Print the batch output.
+  drush_backend_output();
+}
+
+function _drush_unit_batch_operation(&$context) {
+  $context['message'] = "ArrayObject does it job.";
+
+  for ($i = 0; $i < 5; $i++) {
+    drush_print("Iteration $i");
+  }
+  $context['finished'] = 1;
+}
+
+function _drush_unit_batch_finished() {
+  // Restore php limits.
+  // TODO.
+}
