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

时间:2012-09-28 12:01:22

标签: unit-testing refactoring nunit

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

我过于复杂的事情,但我太过于注意看到我做错了什么,或者是另一种做事方式。

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

私有方法本身:

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

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

    • 安排()
    • 法()
    • 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中添加一次,并且将更新包含它的所有公共方法测试。

enter image description here

3 个答案:

答案 0 :(得分:2)

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

我想你有很多常见的设置,拆解,断言和验证可以推广的代码。

答案 1 :(得分:1)

从某种意义上说,你不测试私有方法。您可以彻底测试使用私有方法的方法,并且所有方法都会检出,私有方法“正常”。

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

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

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

并在适当的地方使用它。

答案 2 :(得分:0)

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