Search code examples
unit-testingruby-on-rails-4minitestworkerwebmock

One Test Case Seems to Interfere the Other Test Case


This is on Rails 4.0.11. I am writing a test using minitest and webmock for a worker that interacts with an API. I have two test cases (so far) in my test file. When I run both tests, the second one (the one with "success" in the title) fails. When I comment out the passing test (the one with "retry" in the title), the test case that was failing passes. It seems like the test cases are interfering with each other, but I don't see how.

(I've obfuscated company confidential data in these code examples. Hi boss!)

Here's the test:

require 'test_helper'

class FoocorpCheckStatusWorkerTest < ActiveSupport::TestCase

  REQUEST_HEADERS = {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'text/xml', 'User-Agent'=>'Ruby'}
  EXPECTED_BODY = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<POSBizServices xmlns=\"http://api.foocorp.com\">\n  <POSBizServiceHeader>\n    <userId>ABC</userId>\n    <password>abc123</password>\n    <clientId>123</clientId>\n    <action>CHECKSTATUS</action>\n    <responseFormat>XML</responseFormat>\n  </POSBizServiceHeader>\n  <request>\n    <checkStatus>\n      <ecpdId>123</ecpdId>\n      <refNum>12345678</refNum>\n    </checkStatus>\n  </request>\n</POSBizServices>\n"

  def sample_response_body(order_level_status_code: '', line_level_status_code: '')

    return <<-SAMPLE_RESPONSE_BODY
      <?xml version="1.0" encoding="UTF-8"?>
      <POSBizServices xmlns="http://api.foocorp.com">
        <POSBizServiceHeader>
          <userId>ABC</userId>
          <password>abc123</password>
          <clientId>123</clientId>
          <statusCode>#{order_level_status_code}</statusCode>
          <statusDescription>2015-01-29 14:03:07 [QA13:MS_WSQA4_ONEPOS_01:01] : Request Processed Successfully</statusDescription>
          <action>CHECKSTATUS</action>
          <responseFormat>XML</responseFormat>
        </POSBizServiceHeader>
        <response>
          <statusResponse>
            <count>1</count>
            <orderStatus>
              <orderId>12345678</orderId>
              <orderDate>01/27/2015</orderDate>
              <stats>1,0</stats>
              <aceOrderNo></aceOrderNo>
              <locationCode></locationCode>
              <lineInfoList>
                <count>1</count>
                <lineStatus>
                  <lineSeq>1</lineSeq>
                  <statusCode>#{line_level_status_code}</statusCode>
                  <statusDesc>No Status Yet.</statusDesc>
                </lineStatus>
              </lineInfoList>
            </orderStatus>
          </statusResponse>
        </response>
      </POSBizServices>
    SAMPLE_RESPONSE_BODY
  end

  test "retry codes leave activity processor step in waiting state and activity in auto_processing state" do

    activity_processor_step = activity_processor_steps(:vip_vm_password_reset_step)
    activity = activity_processor_step.activity

    FoocorpVipCheckStatusWorker::STATUS_CODE_MATRIX[:retry].each do |status_code_pair|

      stub_request(:post, FOOCORP_VIP_CONFIG[:base_url]).
        with(:body => /^.*$/,
             :headers => REQUEST_HEADERS
            ).
            to_return(:status => 200, :body => sample_response_body(order_level_status_code: status_code_pair.first, line_level_status_code: status_code_pair.last), :headers => {})

      FoocorpVipCheckStatusWorker.new.perform

      activity.reload
      assert_equal("auto_processing", activity.state)

      activity_processor_step.reload
      assert_equal("waiting", activity_processor_step.state)

    end
  end

  test "success codes complete activity and activity processor step" do

    activity_processor_step = activity_processor_steps(:vip_vm_password_reset_step)
    activity = activity_processor_step.activity

    FoocorpVipCheckStatusWorker::STATUS_CODE_MATRIX[:success].each do |status_code_pair|

      stub_request(:post, FOOCORP_VIP_CONFIG[:base_url]).
        with(:body => /^.*$/,
             :headers => REQUEST_HEADERS
            ).
            to_return(:status => 200, :body => sample_response_body(order_level_status_code: status_code_pair.first, line_level_status_code: status_code_pair.last), :headers => {})

      FoocorpVipCheckStatusWorker.new.perform

      activity.reload
      assert_equal("completed", activity.state)

      activity_processor_step.reload
      assert_equal("completed", activity_processor_step.state)
    end
  end
end

Here's the worker:

class FoocorpVipCheckStatusWorker
  @queue = :short_running

  # Foocorp VIP returns an order-level status code and description
  # and a line-level status code and description. The comments describing
  # the status codes in STATUS_CODE_MATRIX are from Foocorp's Docs.
  #
  # Array format for status codes is:
  # ["(order-level status code)", "(line-level status code)"]

  STATUS_CODE_MATRIX = {
    :success  => [
      ["00057", "00051"]  # Request Processed Successfully
    ],
    :retry    => [
      ["00057", "00053"], # No Status Yet
      ["00057", "00054"], # Order processed successfully but activation is pending.
      ["00099", ""],      # Unexpected error occurred during processing e.g. null pointer exception which causes thread to terminate.
      ["00059", ""],      # Server time out occurred.
      ["10009", ""],      # Back End call returned invalid agent data.
      ["10010", ""],      # Application fails to load ECPD profile details.
      ["00058", "10013"], # Backend calls fails to load Billing information for account. It may be due to some internal issue.
      ["00058", "10032"], # Exception occurred in Bulk Service write
      ["00058", "10033"], # Error occurred in Bulk Service write for reassign
      ["00058", "10040"], # Sub Account create failed
      ["00058", "10144"]  # UNABLE TO RETRIEVE CREDIT INFORMATION
    ]
  }

  def perform

    ActivityProcessorStep.where(state: "waiting").where("vip_ref_num IS NOT NULL").each do |activity_processor_step|

      client = Remote::Clients::FoocorpVipClient.new(
        :params   => {
          :ecpd_id     => activity_processor_step.activity.carrier_account.parent_account.api_access_id,
          :vip_ref_num => activity_processor_step.vip_ref_num
        }
      )
      response = client.check_status
      #TODO: Remove debugging code
      #binding.pry
      order_status_code = response.first[:order_status_code]
      order_status_desc = response.first[:order_status_desc]
      line_status_code  = response.last[:line_status_code]
      line_status_desc  = response.last[:line_status_desc]

      status_codes      = [order_status_code, line_status_code]
      response_message  = line_status_desc.present? ? line_status_desc : order_status_desc
      response_message  ||= "No status description given"

      success             = STATUS_CODE_MATRIX[:success].detect{ |codes_array| codes_array == [order_status_code, line_status_code]}.present?
      retry_status_check  = STATUS_CODE_MATRIX[:retry].detect{ |codes_array| codes_array == [order_status_code, line_status_code]}.present?
      stale_request       = retry_status_check && Time.now > (activity_processor_step.created_at + activity_processor_step.days_to_expire.days)

      if success
        activity_processor_step.update_activity_status('Complete', nil, nil, response_message)
      elsif stale_request
        activity_processor_step.update_activity_status('Failure', nil, nil, "Activity processor step expired.")
      elsif !retry_status_check
        activity_processor_step.update_activity_status('Failure', nil, nil, response_message)
      end

    end
  end
end

Here's my terminal output:

Running both test cases:

me@domo-kun ~/my-project (feature_foocorp_check_status_worker=)$ be rake test TEST=test/workers/foocorp_check_status_worker_test.rb 
Run options: --seed 58882

# Running tests:

.F

Fabulous tests in 2.009658s, 0.9952 tests/s, 11.4447 assertions/s.

  1) Failure:
FoocorpCheckStatusWorkerTest#test_success_codes_complete_activity_and_activity_processor_step     [/Users/steven/Development/my-company/my_company/test/workers/foocorp_check_status_worker_test.rb:86]:
Expected: "completed"
  Actual: "auto_processing"

2 tests, 23 assertions, 1 failures, 0 errors, 0 skips

Commenting out the first ("retry") test case:

me@domo-kun ~/my-project (feature_foocorp_check_status_worker=)$ be rake test TEST=test/workers/foocorp_check_status_worker_test.rb 
Run options: --seed 14937

# Running tests:

.

Fabulous tests in 3.474386s, 0.2878 tests/s, 0.5756 assertions/s.

1 tests, 2 assertions, 0 failures, 0 errors, 0 skips

When I run both tests together and use binding.pry (commented out here) in the worker to examine the value of response during the "success" test case (the one that fails), I get the following two results. I expect the same result (the second one) in both cases.

Running both test cases:

[1] pry(#<VerizonVipCheckStatusWorker>)> response
=> [{:order_status_code=>"00057",
  :order_status_desc=>"",
  :_summary=>{:order_status_code=>"00057", :order_status_desc=>""}},
 {:line_status_code=>"00053",
  :line_status_desc=>"No Status Yet.",
  :_summary=>{}}]

Commenting out the first ("retry") test case:

[2] pry(#<VerizonVipCheckStatusWorker>)> response
=> [{:order_status_code=>"00057",
  :order_status_desc=>"",
  :_summary=>{:order_status_code=>"00057", :order_status_desc=>""}},
 {:line_status_code=>"00051",
  :line_status_desc=>"No Status Yet.",
  :_summary=>{}}]

The value of response in that last example is what I would expect it to be during the first iteration of the "retry" test case, not during the "success" case.

I've tried moving what was in a setup method into the constants and sample_response_body method you see here. I also tried adding this teardown method, but it didn't help:

def teardown
  WebMock.reset!
end

I'm writing both the worker and the test from scratch, so it's entirely possible that I have a dumb syntax/logic errors somewhere. I apologize for what is probably an overly verbose question, but this really has me tearing my hair out. Your help is appreciated. Thanks.


Solution

  • It turned out that I was caching my responses in an adapter class, so it was ignoring the responses that WebMock was giving it.