简体   繁体   English

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

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

I'm writing unit test codes for an existing project. 我正在为现有项目编写单元测试代码。 The project is in Objective-C and I have to test few functions with a number of inputs to the test cases. 该项目是在Objective-C中,我必须测试几个函数,其中包含对测试用例的大量输入。 For example, I have a test case to test a function calculator where two parameters are inputted. 例如,我有一个测试用例来测试函数计算器,其中输入了两个参数。 Currently I create array to store the set of input values to run the test. 目前我创建数组来存储输入值集以运行测试。 The code used are as follows: 使用的代码如下:

- (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]);
    }

}

I searched for best practices online but was not able to find any. 我在网上搜索了最佳实践,但未能找到任何。 Can someone help me with this? 有人可以帮我弄这个吗? Am I running the test in the right way? 我是否以正确的方式进行测试? What is the best practice to be followed in such cases? 在这种情况下,最佳做法是什么? Should I create a test case for every set of input? 我应该为每组输入创建一个测试用例吗?

Your test class should be targeting one specific thing to test which is the sut (system under test). 您的测试类应该针对一个特定的东西来测试哪个是sut(被测系统)。 In your case the sut variable should be your vcToTest. 在你的情况下,sut变量应该是你的vcToTest。 The way I would go about testing is by dependency injection rather than storing all the arrays you are testing as instance variables. 我将进行测试的方式是通过依赖注入,而不是将您正在测试的所有数组存储为实例变量。 So you would create a test method that takes in the parameters that you want to test. 因此,您将创建一个测试方法,该方法接收您要测试的参数。 This way you don't need to keep creating instance variables, you only create local variables that are relevant to the test method. 这样您就不需要继续创建实例变量,只需创建与测试方法相关的局部变量。

- (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);
}

If I was you I wouldn't create arrays to run the tests. 如果我是你,我不会创建数组来运行测试。 Arrays are messy and become hard to understand what is going on, and easy to make mistakes. 数组很乱,很难理解发生了什么,容易犯错误。 I would create specific test methods that test one thing to make sure the sut is working properly. 我会创建特定的测试方法,测试一件事,以确保sut正常工作。 For example in TDD you create a test method that will fail, then modify your sut to fix this failure in the most simple way. 例如,在TDD中,您创建了一个失败的测试方法,然后修改您的sut以最简单的方式修复此故障。 Usually this means your fix will just return exactly what you are expecting. 通常这意味着您的修复将完全返回您期望的内容。 Then you make another test that tests the exact same thing with a different value, it should now fail because your sut is simply returning what they previous test was looking for. 然后你做另一个用不同的值测试完全相同的东西的测试,它现在应该失败,因为你的sut只是返回他们之前的测试所寻找的东西。 Now you modify your sut again to make both tests pass. 现在你再次修改你的sut以使两个测试都通过。 After this in most situations you won't need any addition tests since it has proven to work in two unique ways. 在大多数情况下,您不需要任何额外测试,因为它已被证明可以两种独特的方式工作。

I know you said you are testing software that was written already, but I strongly recommend you check out Test Driven Development. 我知道你说你正在测试已经编写的软件,但我强烈建议你查看测试驱动开发。 Even if you don't actually apply TDD, you will learn how to create meaningful tests. 即使您实际上没有应用TDD,您也将学习如何创建有意义的测试。 This helped me learn tdd 这有助于我学习tdd

In my experience, the key consideration should be how easy it will be to maintain the test suite over time. 根据我的经验,关键考虑因素应该是随着时间的推移维护测试套件是多么容易。 Your current approach will cause problems down the road in two ways: 您当前的方法将以两种方式引发问题:

  1. If you want to use different numbers for you BMI calculation you need to use a different test class (because you're locking in the values in the setup method). 如果要为BMI计算使用不同的数字,则需要使用不同的测试类(因​​为您将锁定设置方法中的值)。

  2. If you ever decide to use different math or multiple BMI equations you'll have to update the arrays anywhere you're checking those values. 如果您决定使用不同的数学或多个BMI方程,则必须在检查这些值的任何位置更新数组。

What I would suggest is to instead create a CSV or plain text file that has the height, weight, and expected BMI values in it. 我建议改为创建一个CSV或纯文本文件,其中包含高度,重量和预期的BMI值。 That keeps the test data together. 这使测试数据保持在一起。 Then in your test methods, load the file and check your actual BMI against the expected BMI. 然后在您的测试方法中,加载文件并检查您的实际BMI与预期的BMI。

You have the flexibility here to mix and match test data, or use different test files for different BMI equations. 您可以灵活地混合和匹配测试数据,或者为不同的BMI方程使用不同的测试文件。 Personally, I also like the fact that you can keep old data files around as you change things, in case you ever want to rollback or add legacy algorithm support. 就个人而言,我也喜欢这样一个事实,即如果您想要回滚或添加遗留算法支持,您可以在更改内容时保留旧数据文件。

A quick and dirty version would look something like this: 一个快速和脏的版本看起来像这样:

- (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);
    }
}

It's usually best to avoid for-loops in unit test code. 通常最好避免单元测试代码中的for循环。 This rule usually leads to separate assertions. 此规则通常会导致单独的断言。

But in your case, you want to exercise a function with various inputs. 但在您的情况下,您希望使用各种输入来执行功能。 Your approach is not bad at all. 你的方法一点也不差。 We can simplify the arrays by using literals: 我们可以使用文字来简化数组:

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"];

I also normally avoid funny formatting. 我通常也会避免有趣的格式化。 But aligning the columns helps us see the values in a table-like format. 但是对齐列有助于我们以类似表格的格式查看值。

Finally, I wouldn't put these values in setUp unless they're used across multiple tests. 最后,我不会将这些值放在setUp除非它们用于多个测试。

If you need many tests like this, it may be worth exploring a test format that uses actual tables, like Fitnesse . 如果您需要这样的许多测试,可能值得探索使用实际表格的测试格式,例如Fitnesse Table data could be in spreadsheets or in wiki format, driving tests. 表数据可以是电子表格或wiki格式,也可以用于测试。

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

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