diff --git a/README.md b/README.md index 1bbd83a..5833fba 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,35 @@ During the `perform` method, you should have stored the results as instance vari The caller can check the success or failure of the use case by calling the `success?` and `failure?` helper methods. If the use case has had a failure, the errors will be available on the standard `errors` collection. +## Testing +When a use case is used in a class to perform an action or query, it can be handy to use a mock for the use case when testing the class. +To do so, `require 'use_case_mock'` in your specs and use the UseCaseMock class to create the state of the use case after the perform action. This is particularly handy when you want to add errors and keep a stable use case behaviour. +Adding errors to the mock will make the use case failed? **but** not invalid? if `perform!` is called. + +```ruby +require 'spec_helper' +require 'use_case_mock' + +RSpec.describe MyParentClass do + + before do + use_case = UseCaseMock.new(results: []) + use_case.errors.add(:base, 'Could not fetch results from 3rd party API') + + allow(MyUseCase).to receive(:perform) { use_case } + end + + describe '.upload' do + it 'does something' do + MyParentClass.upload('/some/random/file.txt') + + # ... + end + end +end + +``` + ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. diff --git a/lib/use_case_mock.rb b/lib/use_case_mock.rb new file mode 100644 index 0000000..f89c0a7 --- /dev/null +++ b/lib/use_case_mock.rb @@ -0,0 +1,9 @@ +require 'ostruct' + +class UseCaseMock < OpenStruct + include UseCasePattern + + def perform + self + end +end \ No newline at end of file diff --git a/spec/use_case_mock_spec.rb b/spec/use_case_mock_spec.rb new file mode 100644 index 0000000..3691713 --- /dev/null +++ b/spec/use_case_mock_spec.rb @@ -0,0 +1,65 @@ +require "spec_helper" +require "use_case_mock" + +describe UseCaseMock do + let(:mock) { UseCaseMock.new(results: ['result_1', 'result_2']) } + + describe "#perform" do + context "when the including class has defined a perform method" do + it "should call the perform method" do + expect(UseCaseMock).to receive(:perform) + UseCaseMock.perform + end + end + + context "when the including class has defined a perform method and initializer" do + subject(:result){ UseCaseMock.perform(number: 10) } + + it "should call the initializer" do + expect(UseCaseMock).to receive(:new).with(number: 10).and_call_original + result + end + + it "should have no errors" do + expect(result.errors.full_messages).to eq([]) + end + + it "should return the use case object" do + expect(result.class).to eq(UseCaseMock) + end + + it "returns the correct attribute readers" do + expect(result.number).to eql 10 + end + end + + context "with a use case that generates errors" do + subject(:result) { UseCaseMock.perform } + + before do + result.errors.add(:age, "can't be blank") + end + + describe "#success?" do + subject(:success){ result.success? } + it { expect(success).to eq(false) } + end + + describe "#failure?" do + subject(:failure){ result.failure? } + it { expect(failure).to eq(true) } + end + + describe "#errors" do + subject(:errors){ result.errors } + it { expect(errors.full_messages).to eq(["Age can't be blank"]) } + end + + describe "#perform!" do + it "does not raise an error" do + expect { result.perform! }.not_to raise_error(UseCasePattern::ValidationError, "Validation failed: Age can't be blank") + end + end + end + end +end \ No newline at end of file