2015-11-14 4 views
2

У меня есть рубиновое приложение.Rspec константа через наследование

Один класс:

module Transfer 
    class Base 
    (...) 
    private 

    def substract_commissions 
     sender_account.commission INTER_BANK_COMMISSION 
    end 
end 

Еще один:

module Transfer 
    class InterBank < Base 
    INTER_BANK_COMMISSION = 5.00 

    private 

    (...) 
    end 
end 

Я тестирую с Rspec класса Base.

В какой-то момент мне нужно проверить, что sender_account может получить комиссию с аргументом:

expect(sender_account).to receive(:commission).with(the_constant)

Проблема заключается в том, что константа определяется его «сын» InterBank анс, как ожидали эта ошибка возникает:

NameError: 
     uninitialized constant Transfer::Base::INTER_BANK_COMMISSION 

Как решить эту проблему?

+0

какой шаблон вы в конечном итоге используете? –

ответ

3

Вы не определили INTER_BANK_COMMISION в Base, так что вы должны ссылаться на него через класс был определен. Поскольку вы в Transfer модуле, вы должны быть в состоянии использовать InterBank::INTER_BANK_COMMISSION, и вы всегда можете полностью определить это как Transfer::InterBank::INTER_BANK_COMMISSION.


Это, в свою очередь, вы должны более внимательно изучить ваш дизайн. Вы ссылаетесь на константу подкласса (InterBank) в суперклассе (Base), который излишне соединяет суперкласс с его подклассом. Подкласс должен строго быть специализацией его суперкласса, и такие модификации, как удаление подкласса, не должны требовать внесения изменений в суперкласс - в этом случае исчезнет INTER_BANK_COMMISSION, и ничего, унаследовавшее от Base, не сможет быть subtract_commission.

Чистый способ избежать зависимости, чтобы оставить Base#subtract_commissions пустым, а затем заменить его в InterBank:

class Base 
    private 

    def subtract_commission 
    # No default commission 
    end 
end 

class InterBank < Base 
    INTER_BANK_COMMISSION = 5.00 

    private 

    def subtract_commission 
    sender_account.commission INTER_BANK_COMMISSION 
    end 
end 

Таким образом, Base ничего не знает о InterBank и не имеет никаких комиссий и InterBank самостоятельно вычитает комиссию, особенно необходимо для его типа передачи.

Комиссия вычитание поведение выглядит как он будет разделяться многими подклассами Base, так что вы можете поднять, что резервное копирование и оставить подклассы нуждаясь только декларировать свои комиссии:

class Base 
    private 

    def commission 
    0 
    end 

    def subtract_commission 
    sender_account.commission commission 
    end 
end 

class InterBank < Base 
    INTER_BANK_COMMISSION = 5.00 

    private 

    def commission 
    INTER_BANK_COMMISSION 
    end 
end 

Теперь вы не необходимо переопределить вычитание в каждом подклассе, каждый из которых объявляет только сумму комиссионных за его тип трансферных сборов. Новые подклассы легко определить и правильно независимы друг от друга.

Вы можете даже ссылаться на константу каждого класса напрямую, если хотите, без необходимости в методе commission.

class Base 
    COMMISSION = 0.00 

    private 

    def subtract_commission 
    sender_account.commission self.class::COMMISSION 
    end 
end 

class InterBank 
    COMMISSION = 5.00 
end 

self.class решает Base или InterBank, а затем каждый определяет свою собственную COMMISSION. Это хорошо, потому что теперь вы можете ссылаться на любую комиссию так же, как и на <class>::COMMISSION, вместо того, чтобы иметь избыточные имена InterBank::INTER_BANK_COMMISSION.

1

Прежде всего, я согласен со всем. Kristján сказал в his answer в отношении рефакторинга вашего кода, и я думаю, вам стоит подумать над тем, что он написал, чтобы улучшить его.

Сказав это, я считаю ключом к получению некоторых прохождений с наименьшим количеством изменений кода. (не может подчеркнуть этот факт) находится в его последней точке: ссылайтесь на константу каждого класса непосредственно, изменив линию sender_account.commission INTER_BANK_COMMISSION к sender_account.commission self.class::INTER_BANK_COMMISSION

Здесь есть некоторые функции, которые охватывают родительский и дочерний класс:

module Transfer 
    class Base 
    private 

    def subtract_commissions 
     sender_account.commission(self.class::INTER_BANK_COMMISSION) 
    end 
    end 

    class InterBank < Base 
    INTER_BANK_COMMISSION = 5.00 
    end 
end 

module Transfer 
    RSpec.describe Base do 
    let(:transfer_base) { described_class.new } 

    describe '#subtract_commissions' do 
     # Substitute this test double out for whatever an actual 
     # sender account looks like in your application 
     let(:sender_account) { double('sender_account') } 

     before do 
     # The INTER_BANK_COMMISSION constant has not been defined 
     # in Base, so to get the test to pass, it needs to be stubbed out 
     stub_const('Transfer::Base::INTER_BANK_COMMISSION', 0.00) 
     allow(transfer_base).to \ 
      receive(:sender_account).and_return(sender_account) 
     end 

     it 'sends a commission to the sender account' do 
     expect(sender_account).to \ 
      receive(:commission).with(described_class::INTER_BANK_COMMISSION) 
     transfer_base.send(:subtract_commissions) 
     end 
    end 
    end 

    RSpec.describe InterBank do 
    let(:transfer_interbank) { described_class.new } 

    describe '#subtract_commissions' do 
     let(:sender_account) { double('sender_account') } 

     before do 
     allow(transfer_interbank).to \ 
      receive(:sender_account).and_return(sender_account) 
     end 

     it 'sends a commission to the sender account' do 
     expect(sender_account).to \ 
      receive(:commission).with(described_class::INTER_BANK_COMMISSION) 
     transfer_interbank.send(:subtract_commissions) 
     end 
    end 
    end 
end 
  • спецификации выше непосредственно проверить substract_commissions, который является частным методом для классов. Я предполагаю, что в вашем приложении у вас есть общедоступный метод, который вызывает substract_commissions, и это тот метод, который вы должны поставить под тест
  • sender_account - это двойной тест в спецификации, поскольку я не мог сказать, какой объект он был получен из приведенной информации, поэтому вы, вероятно, захотите проигнорировать эту часть в своих собственных характеристиках
  • Спектр на Base проходит только потому, что выровняна константа INTER_BANK_COMMISSION. Если вы действительно используете экземпляр класса Base, и вы вызываете метод, который вызывает subtract_commissions, вы получите ошибки, поэтому, пожалуйста, подумайте о повторном посещении вашего дизайна.
Смежные вопросы