Skip to content

Add a metadata field, with feature and unit tests

Mark Bussey edited this page Feb 1, 2019 · 37 revisions

Goals

Setup

(OPTIONAL) Save your current changes

If you have changes in your current branch -- you can check on this via git status -- you'll want to save those before starting this lesson (which uses a separate branch):

git checkout -b work_in_progress`
git add .`
git commit -m 'checkpoint before beginning first metadata lesson'`

Check out working branch

git checkout rspec_setup

NOTE: If you make experimental changes and want to get back to the minimal code state necessary to run this lesson, you can check the starting code out again using:
git checkout rspec_setup

Lesson

Analyze a feature spec for adding a Work

  1. The Hyrax work generator creates a default feature test for each work type you create. As part of the setup for this tutorial, we already ran the generator to create an Image work type. This feature test can be found in spec/features/create_image_spec.rb. We've copied the most relevant part below:

    scenario do
      visit '/dashboard'
      click_link "Works"
      click_link "Add new work"
    
      # If you generate more than one work uncomment these lines
      # choose "payload_concern", option: "Image"
      # click_button "Create work"
    
      expect(page).to have_content "Add New Image"
      click_link "Files" # switch tab
      expect(page).to have_content "Add files"
      expect(page).to have_content "Add folder"
      within('span#addfiles') do
        attach_file("files[]", "#{Hyrax::Engine.root}/spec/fixtures/image.jp2", visible: false)
        attach_file("files[]", "#{Hyrax::Engine.root}/spec/fixtures/jp2_fits.xml", visible: false)
      end
      click_link "Descriptions" # switch tab
      fill_in('Title', with: 'My Test Work')
      fill_in('Creator', with: 'Doe, Jane')
      fill_in('Keyword', with: 'testing')
      select('In Copyright', from: 'Rights statement')
    
      # With selenium and the chrome driver, focus remains on the
      # select box. Click outside the box so the next line can't find
      # its element
      find('body').click
      choose('image_visibility_open')
      expect(page).to have_content('Please note, making something visible to the world (i.e. marking this as Public) may be viewed as publishing which could impact your ability to')
      check('agreement')
    
      click_on('Save')
      expect(page).to have_content('My Test Work')
      expect(page).to have_content "Your files are being processed by Hyrax in the background."
    end
    
  2. This feature test interacts with the browser. Try to complete each step of the feature test manually in your browser. Do you think this test should pass?

  3. Run your test suite:

    rails ci

    OR Run your feature test all by itself:

    rspec spec/features/create_image_spec.rb

Test Driven Development

A key practice of Test Driven Development is to write tests that describe (specify or spec) the desired behavior of our application before writing the code that implements those behaviors. This has multiple benefits:

  1. It requires us to think about what the application needs to do before we dive into how to do it.
  2. It yields a set of automated tests that can be used to audit for unanticipated changes (regressions) in application behavior as we extend and enhance the application.

We're going to follow this model below by writing a test that describes what we want to happen first, and then writing the code makes the test pass - i.e. implements the feature.

Add a metadata field

An Image, in addition to title, creator, keyword, and the metadata fields that are common to all Hyrax objects, also has some Image specific metadata we'd like to track. For our image, we want to save the year, size (original physical dimensions), and latitude and longitude if they are available. Because Fedora 4 stores content as linked data, we need both the name of the field we want to add, and the linked data predicate we want to use to store it. In this example, we're going to add:

Add a new metadata field to our feature spec

  1. Add these lines to spec/features/create_image_spec.rb after the copyright selection:
click_link("Additional fields")
fill_in "Year", with: "2005"
  1. Run your test suite again. It will fail with the error:
$ rails ci # or rails spec

. . . 

Failure/Error: fill_in 'Year', with: '2005'

Capybara::ElementNotFound
  Unable to find visible field "Year" that is not disabled

Add a new metadata field to our model

Recall that Rails uses the MVC (model/view/controller) pattern. In order to add a field to our object, we first need to add it to the Image model.

So far we've only been writing feature specs, also sometimes called integration tests. These are a kind of automated test that exercises many parts of the application at once. For example, our create_image_spec feature is testing whether:

  1. we can create a user and log in
  2. a logged in user can access the new Image url
  3. the expected fields are present on that page
  4. a file can be attached
  5. an Image can be submitted without error given a set of metadata and a file
  6. a new page will be displayed saying the Image has been submitted
  7. that new page will contain the expected metadata

In contrast, we are now going to write a unit test, which will only test one thing: Whether the Image model has a year field.

  1. Open spec/models/image_spec.rb. Notice that this is a stub test that was auto-generated when we created our Image work type. Replace lines 6 - 8 (the it "has tests" do bits inside the RSpec.describe block) with this:
  describe "#year" do
    context "with a new Image" do
      it "has no year value when it is first created" do
        image = Image.new
        expect(image.year).to be_empty
      end
    end

    context "with an Image that has a year defined" do
      it "can set and retrieve a year value" do
        image = Image.new
        image.year = ["2005"]
        expect(image.year).to eq(["2005"])
      end
    end
  end
  1. Run your Image spec again (rspec spec/models/image_spec.rb) . Now you have three failing tests! Our unit tests are failing with an error something like NoMethodError: undefined method 'year'

  2. Edit app/models/image.rb and add this line at the bottom of the class block, but before the line that says include ::Hyrax::BasicMetadata:

property :year, predicate: "http://www.europeana.eu/schemas/edm/year"
  1. Run your Image spec again. Now your unit test should pass. However, your feature test is still failing.

Add a new metadata field to our form

At this point, our Image model has the field we want to add but that field isn't being shown on our new Image form.

  1. As before, we will add our test first. Edit spec/forms/hyrax/image_form_spec.rb and replace the parts inside the Rspec.describe block with:
  subject { form }
  let(:image)    { Image.new }
  let(:ability) { Ability.new(nil) }
  let(:request) { nil }
  let(:form)    { described_class.new(image, ability, request) }
  it "has the expected terms" do
    expect(form.terms).to include(:title)
    expect(form.terms).to include(:year)
  end
  1. Run your test suite and notice that the image_form_spec is now failing:
Failure/Error: expect(form.terms).to include(:year)
  1. Edit app/forms/hyrax/image_form.rb and add this line:
self.terms += [:year]
  1. Run your test suite again (rake spec) and all your tests, including your feature test, should now pass.

Pair exercise (time allowing)

Add an additional metadata field to the form. Be sure to include feature and unit tests.

For discussion: