如何对使用相同私有方法的公共方法进行单元测试?

3
我需要关于软件测试的指导。
我过于复杂化了事情,但我太固执了,看不到我做错了什么,或者有其他方法可以完成任务。
我有几个公共方法都使用同一个私有方法。
私有方法本身:
- 必须处理其特定角色的许多场景 - 与同一实例中的字段/属性/方法密切配合
假设私有方法需要5个测试用例来覆盖所有场景,并且被6个公共方法使用。
问题:
- 我是否需要至少5x6个测试用例? - 如何重复使用私有方法的测试用例来测试每个公共方法? - 是否有任何关于重构重复测试的示例/文章?
例如:
OnStartup()
- if_file_exists_load_entries           ()
- if_file_missing_load_last             ()
- if_no_files_create_new_entries        ()
- if_exception_clear_entries_and_log    ()
- loaded_entries_init_called            ()
- Other tests

OnUserLoadCustom()

- if_file_exists_load_entries           _AND_STORE_AS_LAST()
- if_file_missing_load_last             _AND_STORE_AS_LAST_AND_WARNING_MESSAGE()
- if_no_files_create_new_entries        _AND_WARNING_MESSAGE()
- if_exception_clear_entries_and_log    _AND_ERROR_MESSAGE()
- loaded_entries_init_called            _AND_SUCCESS_MESSAGE()
- Other tests

OnRecover()

- if_file_exists_load_entries           _AND_INFO_MESSAGE()
- if_file_missing_load_last             _AND_INFO_MESSAGE()
- if_no_files_create_new_entries        _AND_INFO_MESSAGE()
- if_exception_clear_entries_and_log    _AND_ERROR_MESSAGE_AND_SHUTDOWN()
- loaded_entries_init_called            _AND_SUCCESS_MESSAGE()
- Other tests

我考虑使用策略模式封装私有方法,这样我就可以独立地测试它(以及公共方法)。

然而,我不想使用它的原因是:

  • 我没有意图在运行时具有可互换的行为
  • 仅仅为了更容易地进行测试而使用该模式似乎是错误的

更新 #1

我的问题涉及测试私有方法行为的公共接口。但是我最终会得到很多重复的测试方法(请参见上面的示例)。

使用策略模式,我认为我只需要:

  • 测试策略中的所有路径(本质上是测试私有方法)
  • 验证所有公共方法都调用了策略(可以轻松使用模拟对象,在此处验证是否已被调用)

但正如我所提到的,我认为我不应该仅仅为了更容易地进行测试而引入一个模式。除非我真的必须采用这种方法。


更新 #2

减少重复的第一次尝试:

  • 将私有方法测试分组到自己的类中(Behaviour1Test)

    • GetTestCases() 返回与此行为相关的测试用例列表
  • 需要此测试的所有公共方法实现公开接口

    • Arrange()
    • Act()
    • Assert()

例如:

// Public method tests
[TestFixture]
public class PublicMethodTests: IBehaviour1Test
{
    // Behaviour 1
    Behaviour1Test _behaviour1;
    IEnumerable<TestCaseData> Behaviour1TestCases{ get { return _behaviour1.GetTestCases(); } }
    [Test]
    [TestCaseSource("Behaviour1TestCases")]
    public void RunBehaviour1Test(Action<IBehaviour1Test> runTestCase)
    {
        runTestCase(this);
    }

    // ==============================
    // Behaviour 1 Arrange/act/assert
    void IBehaviour1Test.Arrange(){}
    void IBehaviour1Test.Assert(object result){}
    object IBehaviour1Test.Act()
    {
        return _model.PublicMethod();        
    }

    // Other tests
}

// Implement this in order to run Behaviour1 test cases
interface IBehaviour1Test
{
    void Arrange(); 
    object Act();
    void Assert(object retValue);
}

// Collection of tests for behaviour 1
public class Behaviour1Test
{
    // Generate test cases
    IEnumerable<TestCaseData>() GetTestCases()
    {
        yield return new TestCaseData((Action<IBehaviour1Test>)Test_1);
        yield return new TestCaseData((Action<IBehaviour1Test>)Test_2);
        yield return new TestCaseData((Action<IBehaviour1Test>)Test_3);
    }

    void Test_1(IBehaviour1Test impl)
    {
        // Arrange
        impl.Arrange(); // public method's arrange
        Test1Setup(); // Arrange for this test

        // Act
        var result = impl.Act();

        // Assert
        Test1Assert(result); // Assert for this test
        impl.Assert(result); // Assert on public method

    }

    void Test_2(IBehaviour1Test impl){}
    void Test_3(IBehaviour1Test impl){}
}

这样,如果我需要为私有方法的行为添加新的测试用例,我只需要在“BehaviourTest”中添加一次,所有包含它的公共方法测试都将被更新。

你不需要测试私有方法,只需测试公共方法。对于您想要测试的所有场景,可以传入尽可能多的数据。 - pengibot
如果你真的需要确保那些方法被调用,你可能想将这些方法提取到另一个类中,然后让你当前的类依赖于它。编辑:就像你提到的策略模式 :) - Chris Missal
3个回答

2

在测试中,你应该像编写应用程序代码一样采用相同的理念。如果可以将常见功能提取到帮助方法或类中,则应该这样做。它将节省你的代码重复,并且如果私有方法更改,它将帮助你进行重构。

我想你可能有很多通用的设置、拆卸、断言和验证代码可以泛化。


现在我想了想,你说得完全正确!我的真正问题与单元测试无关,而是如何删除重复的代码。这让我深思。 - user1705081

1
在某种意义上,您不会测试私有方法。您要彻底测试使用私有方法的方法,并且如果一切正常,则私有方法“有效”。
如果您真的想放心,可以使用反射来执行私有方法,这样您就可以在隔离环境中测试它。
关于测试重构,您当然应该设置和拆卸例程来处理这些相应的功能。我不会在测试中涉及策略模式,只需在测试类中创建非测试方法,其中包含您需要用于多个测试的功能块,并适当使用它们。例如,如果您的私有方法可能会影响5个字段,并且您需要多次测试这5个字段,则会创建一个类似以下内容的方法:
private void verifyField(expected1, extpected2, ....){
     equals(actual1, expected1, 'expected1 correct);
     ....
}

并在适当的地方使用它。


我的问题恰恰在于我不想测试私有方法,而是使用它的公共方法。我的公共方法共享一些常见行为(由私有方法捕获)。正如你所说,我应该测试公共接口。但这会导致我的测试中出现许多重复代码(请参见上面的示例)。我在每个公共接口中重复相同的常见行为测试。我想知道是否有更好的方法来做这件事。 - user1705081

0
你需要将私有方法提取到一个帮助类中。然后单独测试其功能并在其他测试中模拟它。私有方法内的功能与您尝试在其他方法中测试的内容无关,它只提供输入。大多数私有方法通常只是等待被释放的辅助方法。

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