4

Most core and many contrib simpletest test create nodes by preparing the interface, logging an admin in and then filling out the form on /node/add/foo. Not only is this terribly slow, it fails at the most basic rule of Testing: only test your own concerns; mock and stub the rest.

For a test, all I need is N nodes with X tags (taxonomy-terms) attached to them. I am (in no way) interested in whether or not some "Save" button exists somewhere; which is what I am testing when I follow along with core tests, e.g my helper function createNodesWithTags().

This helper is modeled after TaxonomyRSSTestCase which has the same concern as my: make sure there are some nodes with tags, so that some interface can then be tested using these nodes. The helper first creates an entity-field on the node type "article", required because otherwise one cannot add tags to that node. It then proceeds to open up the node/add/article-page, attaches the term and presses Save.

  private function createNodesWithTags($amount) {
    $field = array(
      'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
      'type' => 'taxonomy_term_reference',
      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
      'settings' => array(
        'allowed_values' => array(
          array(
            'vocabulary' => $this->vocabulary->machine_name,
            'parent' => 0,
          ),
        ),
      ),
    );
    field_create_field($field);

    $this->instance = array(
      'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
      'bundle' => 'article',
      'entity_type' => 'node',
      'widget' => array(
        'type' => 'options_select',
      ),
      'display' => array(
        'default' => array(
          'type' => 'taxonomy_term_reference_link',
        ),
      ),
    );
    field_create_instance($this->instance);

    //Create N nodes and attach all N to tag1, N-1 to tag2, N-2 to tag3 etc.
    $attachable = $this->tags;
    for ($i = 0; $i < $amount; $i++) {
      // Post an article.
      $edit = array();
      $langcode = LANGUAGE_NONE;
      $edit["title"] = $this->randomName();
      foreach($attachable as $tag) {
        $edit[$this->instance['field_name'] . '[' . $langcode . '][]'] = $tag->tid;
      }
      $this->drupalPost('node/add/article', $edit, t('Save'));

      //remove one tag, so the next node gets one less tag attached.
      array_shift($attachable);
    }
  }

Why do core tests test this way? Why is TaxonomyTestCase concerned with whether or not saving a node works (i.e. an admin can be created, logged in and finds the Save button)? Why don't they just call node_save() or some other API?

Is there a reason why I should implement it this way too, instead of just dropping records in the database, possible trough the node_save() API?

  • 1
    At a guess it's because validation exists at the form level, not the entity level. If you don't need that validation, or are happy with your own, then I can't see any harm in using node_save() manually. The submit handler for the node add form does that anyway after attaching the fields from the form submission, so if you've attached the required fields yourself and validated them you're just doing the same thing but bypassing the form mechanism – Clive Dec 31 '12 at 14:24
  • @Clive: that only moves the question one layer up: if what you say is true, then "why would core tests be interested in running completely unrelated validations"? – berkes Dec 31 '12 at 14:30
  • 1
    There is no answer to this question, I don't think there is a good reason for doing it like this. That's just how a lot of core tests are currently written (UI/integration tests, not unit tests), partially due to technical limitations and partially because people are lazy ;). Submitting a form is easier in a test than creating a valid node object and saving it. – Berdir Jan 1 '13 at 18:16
  • 4
    Also, in regards to "Not only is this terribly slow, it fails at the most basic rule of Testing: only test your own concerns; mock and stub the rest.". That is the basic rule of Unit Testing, not Testing in general. And writing unit tests in Drupal 7 impossible for anything that is not trivial. – Berdir Jan 1 '13 at 18:18
  • @Berdir: maybe take this into its own answer. I think you are wrong, but this is not the place to discuss that. :) – berkes Jan 2 '13 at 9:54
4

Here is what the Drupal documentation says on it's simpletest page.

Testing methodology Drupal testing focuses on functional testing instead of unit testing. What this means is that the tests are written in such a way that they test the interface as a whole instead of testing individual functions or finite pieces of code. Drupal focuses on functional testing because it is more effective for the way Drupal is written. Functional testing ends up being just as effective if not more effective than unit testing and especially so due to the code style used in Drupal. The testing framework is geared towards this type of testing and the documentation focuses on writing functional tests.

Drupal tests are almost always functional tests and not unit tests. As quoted above, Drupal was coded in such a way (e.g. hooks system, forms api, etc) that Unit tests generally don't make sense.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.