I keen to build sample app, but am I on the right track or missing the point?
Given a part of ordering system where, among other features, user can create
an order and make a payment. And given basic rails setup with extra gems for
enabling rich UI. I'd like to explore how would tests looks like.
Quick, high level, look into architecture reveals the following key layers:
* Rich UI (REST client?)
* CRUD (REST server?)
* Yet another domain (whatever...)
While not as RESTful as defined by Roy T. Fielding, Rails embraces resource
as a central model and makes it universal communication entity which crosses
all layers. It forces us to define resources sooner and maintaining low coupling
to its structure in every layer including tests.
And as long as it makes sense, I imagine the tests would looks like this:
### End-To-End
Covers observable application behavior and interaction with 3rd party services.
```ruby
describe "pay full amount" do
let(:order_page) { application.open order_record }
let(:order_record) { an_order.with(a_line.with_price(100)).create }
before do
order_page.make_payment amount: 100
end
it 'marks order as paid and sends to the manufacturing queue' do
expect(order_page).to have_status_paid
expect(manufacturing_queue).to have_received order_record
end
end
```
* `order_record` - persisted active record model
* `order_page` - manipulates and assert UI (capybara page wrapper)
* `manufacturing_queue` - external service
### Rich UI
This layer enables interaction with the system in a convenient for the humans
way. Basically it consists of a set of UI components, translates user
activities into requests to the next layer and interprets responses.
The test for this layer would share matchers with end-to-end scenarios and might look like this:
```ruby
# creating order
describe "when create button clicked" do
let(:new_order_record) { an_order.build }
before do
order_page.create_order new_order_record
end
it 'makes request' do
expect(application).to have_received_request :create, new_order_record
end
end
# and for making payments
describe "when pay button clicked" do
let(:order_record) { an_order.create }
let(:new_payment_record) { a_payment.build }
before do
order_page.open order_record
order_page.make_payment new_payment_record
end
it 'makes request' do
expect(application).to have_received_request :create, [order_record, new_payment_record]
end
end
```
* `new_order_record` - non persisted active record model
* `new_payment_record` - non persisted active record model
* `order_record` - persisted active record model
* `order_page` - manipulates and assert UI (capybara page wrapper)
* `application` - rails application runner (or wrapper)
* `have_received_request` - custom matcher which decodes request details from
the type and active record objects (similar to respond_with from Responders gem)
### CRUD
Provides resource manipulation interface.
Basically level two in Richardson's maturity model and Rails fully covers
this layer, i.e. form input to output, from handling request/response,
to persisting resources in database of choice.
The test for this layer might look like this:
```ruby
describe "POST /order/{id}/payment", type: :request do
it 'creates resource' do
make_request :create, [order, new_payment]
expect(response).to have_http_status(:created)
expect(response_json).to include(
id: a_kind_of(Integer)
)
end
it 'makes user request' do
expect(user_request_listener).to receive(:process_payment).with(
gateway_id: user.gateway_id,
amount: order.amount
)
make_request :create, [order, new_payment]
end
end
```
* `new_*` - not persisted active record model
* `order` - persisted active record model
* `make_request` - helper method, makes HTTP request based on the type and
resources specified (similar to respond_with from Responders gem)
* `user_request_listeners` - mock object
### Yet another domain
This is where "interesting" things happens as a side effect of resource manipulation.
For example: payment resource creation would trigger payment processing behavior
or "return order" creation would trigger behavior responsible for adjusting
stock levels, cost prices, etc.
Test for this layer usually dive into details of business domain, covering
rules, validations, exceptions, etc.
Does it make sense at all?
Please let me know if any questions.
I'd appreciate any opinions and criticism.
Thank you in advance!