diff --git a/addressfield.info b/addressfield.info
index c074380..271d89b 100644
--- a/addressfield.info
+++ b/addressfield.info
@@ -5,4 +5,5 @@ package = Fields
 
 dependencies[] = ctools
 
+files[] = addressfield.test
 files[] = views/addressfield_views_handler_filter_country.inc
diff --git a/addressfield.test b/addressfield.test
new file mode 100644
index 0000000..874a0ee
--- /dev/null
+++ b/addressfield.test
@@ -0,0 +1,190 @@
+<?php
+
+/**
+ * @file
+ * Addressfield tests.
+ */
+
+
+/**
+ * Common parent class containing common helpers.
+ */
+abstract class AddressfieldWebTestCase extends DrupalWebTestCase {
+
+  /**
+   * Creates a new Addressfield field.
+   */
+  protected function createAddressfield($name) {
+    $field = array(
+      'field_name' => $name,
+      'type' => 'addressfield',
+      'module' => 'addressfield',
+    );
+
+    return field_create_field($field);
+  }
+
+  /**
+   * Attaches an Addressfield field to the user entity.
+   */
+  protected function createAddressfieldInstance($instance) {
+    $instance['widget'] = array(
+      'weight' => 7,
+      'type' => 'addressfield_standard',
+      'module' => 'addressfield',
+      'active' => 1,
+      'settings' => array(
+        'available_countries' => array(),
+        'format_handlers' => array('address' => 'address'),
+      ),
+    );
+
+    return field_create_instance($instance);
+  }
+
+  /**
+   * Asserts that a country was selected.
+   */
+  protected function assertCountrySelected($country_code, $message) {
+    $this->assertRaw('<option value="' . $country_code . '" selected', $message);
+  }
+}
+
+/**
+ * Test AJAX functionality.
+ */
+class AddressfieldAJAXTestCase extends AddressfieldWebTestCase {
+
+  /**
+   * Machine name of the field.
+   */
+  protected $field_name = 'field_address';
+
+  /**
+   * Form element name for the country field.
+   */
+  protected $country_name = '';
+
+  /**
+   * Form element name for the administrative area field.
+   */
+  protected $state_name = '';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Addressfield AJAX',
+      'description' => 'Tests AJAX functionality.',
+      'group' => 'Addressfield',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('addressfield');
+
+    // Create an Addressfield field.
+    $field = $this->createAddressfield($this->field_name);
+
+    // Attach the field to the User entity and require it on registration.
+    $field['settings'] = array('user_register_form' => 1);
+    $field['entity_type'] = 'user';
+    $field['label'] = 'Address';
+    $field['bundle'] = 'user';
+    $field['required'] = TRUE;
+    $this->createAddressfieldInstance($field);
+
+    // Set the US to the site's default country.
+    variable_set('site_default_country', 'US');
+
+    // Define some useful triggering elements and form input names.
+    $country = array(LANGUAGE_NONE, 0, 'country');
+    $this->country_name = $this->field_name . '[' . implode('][', $country) . ']';
+    $state = array(LANGUAGE_NONE, 0, 'administrative_area');
+    $this->state_name = $this->field_name . '[' . implode('][', $state) . ']';
+  }
+
+  function tearDown() {
+    // Delete the field generated in setUp().
+    $instance = field_info_instance('user', $this->field_name, 'user');
+    field_delete_instance($instance, TRUE);
+
+    parent::tearDown();
+  }
+
+  /**
+   * Tests basic AJAX functionality.
+   */
+  public function testBasicAJAX() {
+    // Visit the user registration page.
+    $this->drupalGet('user/register');
+
+    // Ensure the Addressfield is there.
+    $this->assertField($this->country_name, t('Addressfield field placed on user registration form.'));
+
+    // Ensure the US is selected as the default country.
+    $this->assertCountrySelected('US', t('United States selected as default country.'));
+
+    // Set the country to Canada via AJAX.
+    $form_input[$this->country_name] = 'CA';
+    $this->drupalPostAJAX(NULL, $form_input, $this->country_name);
+
+    // Ensure Canada is now the selected country.
+    $this->assertCountrySelected('CA', t('Canada now selected as country.'));
+
+    // Ensure the "state" label has been renamed to "province."
+    $this->assertText('Province', t('Province label replaces state label.'));
+  }
+
+  /**
+   * Tests proper suppression of validation errors on AJAX form submission.
+   */
+  public function testErrorSuppressionAJAX() {
+    // Enable Drupal page cache and set cache minimum lifetime.
+    variable_set('cache', 1);
+    variable_set('cache_lifetime', 3600);
+
+    // Visit the user registration page; this will be a cache MISS.
+    $this->drupalGet('user/register');
+
+    // The default country chosen was US; simulate the user selecting WA.
+    $form_input[$this->country_name] = 'US';
+    $form_input[$this->state_name] = 'WA';
+
+    // Simulate the user now selecting Brazil (performing an AJAX POST).
+    $form_input[$this->country_name] = 'BR';
+    $this->drupalPostAJAX(NULL, $form_input, $this->country_name);
+
+    // Ensure Brazil is now the selected country.
+    $this->assertCountrySelected('BR', t('Brazil now selected as country.'));
+
+    // Ensure users can select Brazilian administrative areas.
+    $this->assertText('Sao Paulo', t('Brazilian administrative areas present in form.'));
+
+    // Visit the user registration page again; This will be a cache HIT.
+    $this->drupalGet('user/register');
+    $headers = $this->drupalGetHeaders();
+    $this->assertTrue($headers['x-drupal-cache'] == 'HIT', t('Page returned a cache HIT.'));
+
+    // The default country should be the US; simulate the user selecting WA.
+    $this->assertCountrySelected('US', t('United States selected as default country.'));
+    $form_input[$this->country_name] = 'US';
+    $form_input[$this->state_name] = 'WA';
+
+    // Select a different country with a select field for country.
+    $form_input[$this->country_name] = 'IT';
+    $ajax_commands = $this->drupalPostAJAX(NULL, $form_input, $this->country_name);
+
+    // Ensure users can select Italian administrative areas.
+    $this->assertText('Venezia', t('Italian administrative areas present in form.'));
+
+    // Ensure there are no errors on the administrative area field.
+    foreach ($ajax_commands as $command) {
+      if (isset($command['data'])) {
+        $this->assertTrue(strpos($command['data'], t('Error message')) === FALSE, t('No validation errors inserted after country selection.'));
+      }
+    }
+
+    // Revert page cache settings to defaults.
+    variable_set('cache', 0);
+    variable_set('cache_lifetime', 0);
+  }
+}
\ No newline at end of file
