繁体   English   中英

目标C - 具有多个测试输入的单元测试的最佳实践

[英]Objective C - Best practice for Unit Testing with multiple test inputs

我正在为现有项目编写单元测试代码。 该项目是在Objective-C中,我必须测试几个函数,其中包含对测试用例的大量输入。 例如,我有一个测试用例来测试函数计算器,其中输入了两个参数。 目前我创建数组来存储输入值集以运行测试。 使用的代码如下:

- (void)setUp {
    [super setUp];
    self.vcToTest = [[BodyMassIndexVC alloc] init];
    input1 = [[NSMutableArray alloc] initWithObjects:@"193", @"192", @"192", @"165", @"155", @"154", nil];
    input2 = [[NSMutableArray alloc] initWithObjects:@"37", @"37", @"36", @"80",@"120", @"120", nil];
}



- (void)testCalculatorSuccess {
    for (int i=0; i<input1.count; i++) {
        NSArray *expectedResult = [[NSArray alloc] initWithObjects: @"9.93", @"10.04", @"9.77", @"29.38", @"49.95", @"50.60", nil];
        NSString *actualResult = [self.vcToTest calculateResult:input1[i] andInput2:input2[i]];
        XCTAssertEqualObjects(actualResult, expectedResult[i]);
    }

}

我在网上搜索了最佳实践,但未能找到任何。 有人可以帮我弄这个吗? 我是否以正确的方式进行测试? 在这种情况下,最佳做法是什么? 我应该为每组输入创建一个测试用例吗?

您的测试类应该针对一个特定的东西来测试哪个是sut(被测系统)。 在你的情况下,sut变量应该是你的vcToTest。 我将进行测试的方式是通过依赖注入,而不是将您正在测试的所有数组存储为实例变量。 因此,您将创建一个测试方法,该方法接收您要测试的参数。 这样您就不需要继续创建实例变量,只需创建与测试方法相关的局部变量。

- (void)setUp {
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.
    self.sut = [[BodyMassIndexVC alloc] init];
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    self.sut = nil;
    [super tearDown];
}

- (void)testBMIFormulaInCmAndKgSuccess {
    // local variables that are only seen in this method 
    NSArray *heightsInMetres = [[NSMutableArray alloc] initWithObjects:@"193", @"192", @"192", @"165", @"155", @"154", nil];
    NSArray *weightsInKg = [[NSMutableArray alloc] initWithObjects:@"37", @"37", @"36", @"80",@"120", @"120", nil];
    NSArray *expectedResults = [[NSArray alloc] initWithObjects: @"9.93", @"10.04", @"9.77", @"29.38", @"49.95", @"50.60", nil];
    for (int i=0; i<heightsInMetres.count; i++) {
        [self xTestHeightInMeters:heightsInMetres[i] weightInKg:weightsInKg[i] expecting:expectedResults[i]];
    }
}

// the variables that are used to test are injected into this method
- (void)xTestHeightInMeters:(NSString *)height weightInKg:(NSString *)weight expecting:(NSString *)expectedResult {
    NSString *result = [self.sut calculateBMIforHeight:height andWeight:weight];
    XCTAssertEqual(result, expectedResult);
}

如果我是你,我不会创建数组来运行测试。 数组很乱,很难理解发生了什么,容易犯错误。 我会创建特定的测试方法,测试一件事,以确保sut正常工作。 例如,在TDD中,您创建了一个失败的测试方法,然后修改您的sut以最简单的方式修复此故障。 通常这意味着您的修复将完全返回您期望的内容。 然后你做另一个用不同的值测试完全相同的东西的测试,它现在应该失败,因为你的sut只是返回他们之前的测试所寻找的东西。 现在你再次修改你的sut以使两个测试都通过。 在大多数情况下,您不需要任何额外测试,因为它已被证明可以两种独特的方式工作。

我知道你说你正在测试已经编写的软件,但我强烈建议你查看测试驱动开发。 即使您实际上没有应用TDD,您也将学习如何创建有意义的测试。 这有助于我学习tdd

根据我的经验,关键考虑因素应该是随着时间的推移维护测试套件是多么容易。 您当前的方法将以两种方式引发问题:

  1. 如果要为BMI计算使用不同的数字,则需要使用不同的测试类(因​​为您将锁定设置方法中的值)。

  2. 如果您决定使用不同的数学或多个BMI方程,则必须在检查这些值的任何位置更新数组。

我建议改为创建一个CSV或纯文本文件,其中包含高度,重量和预期的BMI值。 这使测试数据保持在一起。 然后在您的测试方法中,加载文件并检查您的实际BMI与预期的BMI。

您可以灵活地混合和匹配测试数据,或者为不同的BMI方程使用不同的测试文件。 就个人而言,我也喜欢这样一个事实,即如果您想要回滚或添加遗留算法支持,您可以在更改内容时保留旧数据文件。

一个快速和脏的版本看起来像这样:

- (NSArray *)dataFromFileNamed:(NSString *)filename {
    NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:filename ofType:nil];
    // load the data however its formatted
    // return the data as an array
    return loadedData;
}

- (void)testBMIFormulaInCmAndKgSuccess {
    NSArray *testData = [self dataFromFileNamed:@"BMI_data_01.txt"];
    for (int i=0; i < testData.count; i++) {
        NSArray *dataSet = testData[i];
        CGFloat height = dataSet[0];
        CGFloat weight = dataSet[1];
        CGFloat expectedBMI = dataSet[2];
        NSString *actualResult = [self.vcToTest calculateBMIforHeight:height andWeight:weight];
        XCTAssertEqual(actualResult, expectedBMI);
    }
}

通常最好避免单元测试代码中的for循环。 此规则通常会导致单独的断言。

但在您的情况下,您希望使用各种输入来执行功能。 你的方法一点也不差。 我们可以使用文字来简化数组:

NSArray<NSString *> *heightsInMetres = @[ @"193",   @"192",  @"192",   @"165",   @"155",   @"154"];
NSArray<NSString *> *weightsInKg =     @[  @"37",    @"37",   @"36",    @"80",   @"120",   @"120"];
NSArray<NSString *> *expectedResults = @[@"9.93", @"10.04", @"9.77", @"29.38", @"49.95", @"50.60"];

我通常也会避免有趣的格式化。 但是对齐列有助于我们以类似表格的格式查看值。

最后,我不会将这些值放在setUp除非它们用于多个测试。

如果您需要这样的许多测试,可能值得探索使用实际表格的测试格式,例如Fitnesse 表数据可以是电子表格或wiki格式,也可以用于测试。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM