如何测试(TDD)一个单例类?

4
我正在使用Minitest在Ruby应用程序中开始DDD和TDD。我创建了一个仓库类(没有数据库访问,但它为我生成实体)。它是一个单例。
我想测试实体的生成。问题是因为它是单例,测试的执行顺序会影响结果。
有没有办法强制处理单例元素,使其“新鲜”?
这是我的仓库代码:
require "singleton"

class ParticipantRepository
    include Singleton

    def initialize()
        @name_count = 0
    end

    def generate_participant()
        participant = Participant.new
        participant.name = "Employee#{get_name_count()}"
        return participant
    end

    private 
    def get_name_count()
        old_name_count = @name_count
        @name_count += 1
        return old_name_count
    end
end

测试代码:

require_relative 'test_helper'


class ParticipantRepositoryTest < MiniTest::Unit::TestCase

    def setup()
        @repository = ParticipantRepository.instance
    end

    def test_retrieve_participant
       participant = @repository.generate_participant

       refute_nil participant
       refute_nil participant.name
       refute_equal("", participant.name)
       assert_equal(0, participant.subordinates_count)
    end

    def test_employee_name_increment
        participant1 = @repository.generate_participant
        participant2 = @repository.generate_participant

        refute_equal(participant1.name, participant2.name)

        index_participant1 = /Employee([0-9]+)/.match(participant1.name)[1]
        index_participant2 = /Employee([0-9]+)/.match(participant2.name)[1]

        assert_equal(0, index_participant1.to_i)
        assert_equal(1, index_participant2.to_i)
    end

end

当执行test_employee_name_increment时,断言assert_equal(0, index_participant1.to_i)会成功,如果最后执行该测试,则会失败。

我想能够测试存储库(因为它将发展成为更大的东西)。我该怎么做?

谢谢!


单例模式和可测试性常常存在矛盾。考虑使用某种依赖注入来强制实现单例,而不是让类自己担心这个问题。 - Matt Ball
@MattBall 这将会很棒。你有关于如何使用 DI 解决这个问题的参考资料吗? - JSBach
没有具体的要求,我不写Ruby。看一下这个链接:http://stackoverflow.com/search?q=ruby+dependency+injection - Matt Ball
请看我对类似问题的回答 - https://dev59.com/-HI-5IYBdhLWcg3wVWvv#23901644 - Cameron Martin
2个回答

5

测试用例的顺序并不重要。要正确地测试单例类,您需要将其视为实例对象进行处理。为此,请在设置期间将单例包装在匿名类中。每次调用设置时,您都会获得未经触碰的ParticipantRepository副本:

def setup
  @repository = Class.new(ParticipantRepository).instance
end

1
这里列出了一些其他选项(我认为@MicahJaffe的答案也在其中):https://dev59.com/Jmct5IYBdhLWcg3wZMjn#12128594。不过我确实喜欢这个选项。 - Travis Hohl

3

哇,这是一个意外的方法名:p 我会尝试更改我的架构以避免使用它。有什么想法吗? - JSBach
顺便说一下,测试本身并不依赖于顺序。问题在于我期望得到一个特定的值,但所使用的实例不是“新鲜”的,因为它是单例。解决单例问题可以解决顺序依赖问题。 - JSBach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接