【问题标题】:How to unit test public methods that use same private methods?如何对使用相同私有方法的公共方法进行单元测试?
【发布时间】:2012-09-20 07:34:50
【问题描述】:

我的软件测试需要一些指导。

我把事情复杂化了,但我太专注于看自己做错了什么,或者是另一种做事方式。

我有几个公共方法使用相同的私有方法。

私有方法本身:

  • 必须为其特定角色处理许多场景
  • 与同一实例中的字段/属性/方法密切合作

说私有方法需要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() 返回与此行为相关的测试用例列表
  • 所有需要这个测试的公共方法都实现了暴露的接口

    • 排列()
    • Act()
    • 断言()

例如:

// 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 中添加一次,所有包含它的公共方法测试都会更新。

【问题讨论】:

  • 你不测试私有方法,只测试公共方法。为您希望测试的所有场景传递尽可能多的数据
  • 如果您确实需要确保调用这些方法,您可能希望将这些方法提取到当前类需要的另一个类中。编辑:就像你提到的,策略模式:)

标签: unit-testing refactoring nunit


【解决方案1】:

从某种意义上说,您不会测试私有方法。您彻底测试使用私有方法的方法,并且一切都检查出来,私有方法“有效”。

如果你真的想要安心,你可以使用反射来执行私有方法,这样你就可以单独测试它。

关于测试重构,您当然应该有设置和拆卸例程来处理这些相应的功能。我不会在测试中进入策略模式,只需在测试类中创建非测试方法,这些方法具有多个测试所需的功能块并适当地使用它们。例如,如果您的私有方法可以影响 5 个字段,并且您需要多次测试这 5 个字段,我会创建一个类似的方法

private void verifyField(expected1, extpected2, ....){
     equals(actual1, expected1, 'expected1 correct);
     ....
}

并在适当的地方使用它。

【讨论】:

  • 我的问题正是因为我不想测试私有方法,而是使用它的公共方法。我的公共方法有一些共同的行为(被私有方法捕获)。正如你所说,我应该测试公共接口。但这会导致我的测试中有很多重复的代码(参见上面的示例)。我正在对每个公共接口中的常见行为重复相同的测试。我想知道是否有更好的方法来做事。
【解决方案2】:

您应该在测试中应用与在应用程序代码中相同的理念。如果可以将通用功能提取到辅助方法或类中,那么您应该这样做。它将为您节省代码重复,并在私有方法发生更改时帮助您稍后重构。

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

【讨论】:

  • 现在我想起来了,你是绝对正确的!我真正的问题与单元测试无关,而是如何删除重复代码。这让我深思。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    相关资源
    最近更新 更多