Dynamiczne dodawanie/usuwanie scope w testach

Witam, potrzebuje przetestować pewną funkcjonalność która zależy od tego czy scope jest dodana. Model user jest w aplikacji, natomiast testuje engine, stąd takie kombinacje, próbowałem coś takiego

it 'do something' do
  User.class(lub instance)_eval do
      scope :some_users, lambda { where("users.email LIKE '%foo%' ")}
    end

    tutaj test ...

    class << User
      remove_method :some_users
    end

    # reload User model
    Object.send(:remove_const, 'User') 
    load 'user.rb'
end

Test uruchomiony oddzielnie zawsze przechodzi. Natomiast testy przechodzą lub nie, losowo.
Jakieś porady jak to dodać i usunąć poprawnie ? Czy lepiej używać class_eval czy instance_eval ?

  1. instance_eval jeśli jest to wyłącznie dla danej instancji i nie będziesz tego potrzebował w innym miejscu
  2. zamień lambdę na definicję metody klasowej (def self.some_users), łatwiej ci będzie dostać się do unscoped zakresu i może nawet ustawić jakąś osobną zmienną mówiącą czy dany scope został zaaplikowany.

Ale po co miałbym się dostawać do zakresu unscoped ? Ja nie chce sprawdzać czy scope został dodany, tylko chce sprawdzić pewną funkcjonalność, której zwrócone dane, zależą od tego czy scope jest czy go nie ma, działać musi ze scopem czy bez.
Konkretnie chodzi mi o ogranicznienie dostępu do nowej funkcjonalności w aplikacji na produkcji, do pewnej grupy użytkowników (zdefiniowanej w modelu User np za pomocą scope), przykładowo userzy o mailu z wybranej domeny. Może po prostu źle sie za to zabieram ?
Funkcjonalność jest “rozwalona” po paru miejscach, w widoku, w dwóch kontrolerach, jest też mailer, odpowiada za nią engine.

ps, możesz rozwinąć punkt 1, bo nie do końca rozumiem

Nie łatwiej byłoby Ci zrobić stub metody sprawdzajacej / zwracającej email usera?

user.stub(:email_type).and_return :company

Nie rozumiem do końca co chcesz uzyskać, ale wydaje mi się, że używanie instance_eval w ten sposób, to proszenie się o problemy.

Czy mógłbyś rozwinąć trochę opis sytuacji? Tzn. gdzie co się znajduje i jak to dokładnie ma działać? Jakiś pseudo code z tych miejsc, w których tego używasz (widoki, kontrolery, model) mile widziany.

Jest to system odznaczeń zaimplementowany w engine. Testowo tylko pewna grupa użytkowników powinna widzieć kto dostał nowe odznaczenia jak otrzymywać je za wybrane zasługi itp.
Wszystko działa ok, tylko testy wywalają się gdy próbuje symulować dodanie tej scope do modelu.

Właśnie tej części nie rozumiem. Z tego co wiem, po dodaniu scope’a musisz go użyć, tzn. samo zrobienie:

class User
  scope :some_users, lambda { where("users.email LIKE '%foo%' ")}
end

nie daje Ci nic poza tym, że dostajesz nową metodę na klasie User. W jaki sposób aplikujesz tego scope’a w aplikacji?

Po prostu go dodaje w kodzie aplikacji (nie engine). Ale chciałbym mieć uniwersalne testy dla przypadków gdy on jest lub gdy go nie ma. bo docelowo go nie będzie.
Chyba że to przekombinowane i lepiej dać testy i scope a potem po udostępnieniu ficzera dla wszystkich poprawić dostosować testy ?

Ja bym to jakoś wyizolował i zrobił dependency injection, coś w stylu:

def users_for_a_feature(scope = User)
  scope.respond_to?(:some_users) ? scope.some_users : scope
end

Wtedy w testach możesz przekazać inną klasę czy stub/mock object i sprawdzić czy to zostało wywołane.

doszedłem do tego że testy losowo wywalały się z powodu tego kodu

Object.send(:remove_const, 'User') 
load 'user.rb'

Prawdopodobnie dlatego że jest użyty engine. Usunięcie powyższego kodu ze speców pomogło. Natomiast w testach funkcjonalnych zgodnie z radą zmockowałem tą metode. Tak że tylko w modelu jest testowana pełnie.