在Ruby中以惯用方式创建外观

时间:2016-04-06 12:50:32

标签: ruby unit-testing facade

我正在尝试用来自Java的惯用Ruby实现Facade。我可以看到Rails'ActiveRecord喜欢在find_by(criteria)之类的东西上使用类方法,并且不使用Repository模式来完成该任务。

My Facade使用多种方法包装特定的Web服务。我最初的想法是让它的API类似于ActiveRecord(通过模仿学习):

class MyEntity
  # ....

  def get_name
    @loaded_name + @loaded_surname
  end

  def delete
    @entity_access_service.delete(@id)
  end

  def save 
    @entity_access_service.save(@id, @loaded_name , @loaded_surname)
  end

  def self.find(id)
    data = @entity_access_service.get_data_for(id)
    MyEntity.new(data) #Or whatever way to populate my entity
  end
end

理论上,这会很有效:

e = MyEntity.find(10)
p e.get_name
e.delete

或者:

e = MyEntity.new(some stuff)
e.save

问题: 要使savedelete实例方法起作用,我需要以某种方式获取EntityAccessService的实例。此实例应该是可模拟的,以便在隔离环境中对其进行测试。这样做的正确方法是什么?

我希望我的测试看起来尽可能简单,没有一些奇怪的黑客,因为我正在尝试实现它似乎相当微不足道。

我想到了几个选项:

  1. 拥有在应用程序中创建的所有实体使用的entity_access_service的类级变量。在这种情况下,我应该在哪里初始化此字段?例如:

    class MyEntity
      @@entity_access_service = nil
    end
    
    # Somewhere else (where?):
    MyEntity.entity_access_service = MyEntityService.new(some_params_from_env)
    

    这样,在我的测试中,我必须在开始时初始化/模拟它。

  2. 与1类似,但在课堂上初始化它。这看起来很奇怪,特别是如果我知道我的测试根本没有填充所需的ENV参数。

  3. 有一个额外的构造函数/属性来设置entity_service。这不起作用,因为save不会初始化此字段。

  4. 创建Repository课程。这样做会很好,但似乎不是Ruby人所做的。

1 个答案:

答案 0 :(得分:1)

在ActiveRecord的示例之后,您可以在类本身或者从其他类派生的基类上创建方法。

ActiveRecord提供了一个方法ActiveRecord::Base.connection,它返回所有模型用来访问数据库的连接对象。你可以做类似的事情:

class MyEntity
    ....

    def self.entity_access_service
      # return your service object
    end

    def self.find(id)
        MyEntity.entity_access_service.get_data_for(id)
        MyEntity.new(data) # Or whatever way to populate my entity
    end

    def save() 
        MyEntity.entity_access_service.save(@id, @loadedName, @loadedSurname)
    end
end

就初始化而言,您必须在应用程序(和测试套件)中进行初始化步骤,从配置文件中读取服务凭据并将其传递到MyEntity对象,你的entity_access_service方法可以懒惰地创建它在第一次访问时返回的对象,使用一个非常常见的Ruby习语:

def self.entity_access_service
  @entity_access_service || = # build entity_access_service object
end

请注意,通过将类级实例变量包装在类级访问器方法中,可以避免使用@@ recommended best practice