UTPLSQL_vs_RSpec

I’ve finished my previous post a bit too soon and was not precise on the ruby-plsql unite test results analysis.
I’ve decided to dig a bit deeper to validate that ruby-plsql (RSpec) actually support datatype mismatch exceptions where utPLSQL unit testing fails due to oracle implicit datatype conversion.

Retrospective

Lets have a look once more at where we landed.

There is a package with two functions.

CREATE OR REPLACE PACKAGE some_table_api AS

  FUNCTION get_customer_no(
    p_customer_id customers.customer_id%TYPE
  ) RETURN customers.customer_no%TYPE;
  FUNCTION get_customer_valid_flag(
    p_customer_id customers.customer_id%TYPE
  ) RETURN customers.customer_valid_flag%TYPE;

END some_table_api;
/

The package was initially working on the below table structure.

CREATE TABLE customers (
  customer_id NUMBER,
  customer_no NUMBER(10,0),
  customer_valid_flag VARCHAR2(1),
  CONSTRAINT chk_valid_flag CHECK (customer_valid_flag IN ('0','1'))
);

But the table structure was changed:

CREATE TABLE customers_new (
  customer_id NUMBER,
  customer_no VARCHAR2(30),
  customer_valid_flag NUMBER(1,0)
);

INSERT INTO customers_new SELECT * FROM customers;

DROP TABLE customers;

RENAME customers_new TO customers;

and so the datatypes returned by functions have changed.

We have following ruby-plsql unit tests.

describe 'get customer number' do

  before(:all) do
    plsql.customers.insert({customer_id: -1, customer_no: 123456, customer_valid_flag: '1' })
  end

  it 'returns expected customer number as a numeric value' do
    expect( plsql.some_table_api.get_customer_no(-1) ).to eq 123456
  end

  it 'returns expected customer number as a numeric value through assignment' do
    result = plsql.some_table_api.get_customer_no(-1)
    expect( result ).to eq 123456
  end

end

describe 'get customer valid flag' do

  before(:all) do
    plsql.customers.insert({customer_id: -1, customer_no: 123456, customer_valid_flag: '1' })
  end

  it 'returns expected customer validity flag as a character' do
    expect( plsql.some_table_api.get_customer_valid_flag(-1) ).to eq '1'
  end

  it 'returns expected customer validity flag as a character through assignment' do
    result = plsql.some_table_api.get_customer_valid_flag(-1)
    expect( result ).to eq '1'
  end
end

And the tests are now failing.

C:\Users\Jacek\RubymineProjects\utplsql_vs_plsql_spec>rspec spec\some_table_api\*
FFFF

Failures:

  1) get customer number returns expected customer number as a numeric value
     Failure/Error: plsql.customers.insert({customer_id: -1, customer_no: 123456, customer_valid_flag: '1' })
     TypeError:
       can't convert Fixnum into String
     # ./spec/some_table_api/get_customer_no_spec.rb:4:in `block (2 levels) in <top (required)>'

  2) get customer number returns expected customer number as a numeric value through assignment
     Failure/Error: plsql.customers.insert({customer_id: -1, customer_no: 123456, customer_valid_flag: '1' })
     TypeError:
       can't convert Fixnum into String
     # ./spec/some_table_api/get_customer_no_spec.rb:4:in `block (2 levels) in <top (required)>'

  3) get customer valid flag returns expected customer validity flag as a character
     Failure/Error: plsql.customers.insert({customer_id: -1, customer_no: 123456, customer_valid_flag: '1' })
     TypeError:
       can't convert Fixnum into String
     # ./spec/some_table_api/get_customer_valid_flag_spec.rb:4:in `block (2 levels) in <top (required)>'

  4) get customer valid flag returns expected customer validity flag as a character through assignment
     Failure/Error: plsql.customers.insert({customer_id: -1, customer_no: 123456, customer_valid_flag: '1' })
     TypeError:
       can't convert Fixnum into String
     # ./spec/some_table_api/get_customer_valid_flag_spec.rb:4:in `block (2 levels) in <top (required)>'

Finished in 0.025 seconds (files took 1.48 seconds to load)
4 examples, 4 failures

Failed examples:

rspec ./spec/some_table_api/get_customer_no_spec.rb:7 # get customer number returns expected customer number as a numeric value
rspec ./spec/some_table_api/get_customer_no_spec.rb:11 # get customer number returns expected customer number as a numeric value through assignment
rspec ./spec/some_table_api/get_customer_valid_flag_spec.rb:7 # get customer valid flag returns expected customer validity flag as a character
rspec ./spec/some_table_api/get_customer_valid_flag_spec.rb:11 # get customer valid flag returns expected customer validity flag as a character through assignment


C:\Users\Jacek\RubymineProjects\utplsql_vs_plsql_spec>

The thing is, the test did not fail because the function return datatype has changed.
The tests have actually failed on the insert statement due to the fact that the table structure was changed.

That got me thinking. Those little and simple tests already have some dependency to table CUSTOMERS, though the CUSTOMERS table itself is not being tested. The tests should validate API regardless of the table structure.

So what can be done?

Plain SQL initialization

Since it is Ruby that guards datatypes more than Oracle, I can use Oracle to do the inserts and take advantage of the Oracle “feature” of implicit datatype conversion.
Let’s give it a try with get_customer_no function

describe 'get customer number using pure SQL setup' do

  before(:all) do
    plsql.execute <<-SQL
      INSERT INTO customers(customer_id, customer_no,customer_valid_flag)
      VALUES (-1, 123456, '1')
    SQL
  end

  it 'returns expected customer number as a numeric value' do
    expect( plsql.some_table_api.get_customer_no(-1) ).to eq 123456
  end

  it 'returns expected customer number as a numeric value through assignment' do
    result = plsql.some_table_api.get_customer_no(-1)
    expect( result ).to eq 123456
  end

end

And the results of tests are:

C:\Users\Jacek\RubymineProjects\utplsql_vs_plsql_spec>rspec spec\some_table_api\get_customer_no_pure_sql_spec.rb
FF

Failures:

  1) get customer number using pure SQL setup returns expected customer number as a numeric value
     Failure/Error: expect( plsql.some_table_api.get_customer_no(-1) ).to eq 123456

       expected: 123456
            got: "123456"

       (compared using ==)
     # ./spec/some_table_api/get_customer_no_pure_sql_spec.rb:11:in `block (2 levels) in <top (required)>'

  2) get customer number using pure SQL setup returns expected customer number as a numeric value through assignment
     Failure/Error: expect( result ).to eq 123456

       expected: 123456
            got: "123456"

       (compared using ==)
     # ./spec/some_table_api/get_customer_no_pure_sql_spec.rb:16:in `block (2 levels) in <top (required)>'

Finished in 0.03701 seconds (files took 1.75 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./spec/some_table_api/get_customer_no_pure_sql_spec.rb:10 # get customer number using pure SQL setup returns expected customer number as a numeric value
rspec ./spec/some_table_api/get_customer_no_pure_sql_spec.rb:14 # get customer number using pure SQL setup returns expected customer number as a numeric value through assignment


C:\Users\Jacek\RubymineProjects\utplsql_vs_plsql_spec>

After overcoming the tests failure on COUSTOMERS table, the tests are still failing.
The tests have failed on the assertions, as they should. We expected to get an integer and received a string since the datatypes of functions have changed. Cool!

Leave a Reply