Заказ unit test с использованием XCTest в Xcode 6
Я хотел бы знать, есть ли способ сказать Xcode для запуска модульных тестов в указанном порядке. Я имею в виду не в том же файле класса XCTestCase, а между всем файлом класса.
![enter image description here]()
Например, я хочу запустить SitchozrSDKSessionTest перед запуском SitchozrSDKMessageTest.
Я просмотрел несколько потоков в стеке или документацию Apple, и я не нашел что-то полезное.
Спасибо за вашу помощь.
Ответы
Ответ 1
Все они отсортированы по алфавиту (классы и методы). Поэтому, когда вам нужны некоторые тесты, выполняемые последними, просто измените имя класса или метода (для (скверного) примера с помощью префикса "z _" ).
Итак... У вас есть имена классов:
MyAppTest1
-testMethodA
-testMethodB
MyAppTest2
-testMethodC
-testMethodD
и они выполняются в этом порядке. Если вам нужно запустить MyAppTest1 как второй, просто переименуйте его так, чтобы оно было в алфавитном порядке после MyAppTest2 (z_MyAppTest1), и оно будет работать (одинаково для метода):
MyAppTest2
-a_testMethodD
-testMethodC
z_MyAppTest1
-testMethodA
-testMethodB
Также, пожалуйста, возьмите именование в качестве примера:)
Ответ 2
Верно, что в настоящее время (с Xcode 7) методы XCTestCase выполняются в алфавитном порядке, поэтому вы можете заставить их порядок, называя их умным способом. Однако это детализация реализации и кажется, что она может измениться.
A (надеюсь) менее хрупкий способ сделать это - переопределить +[XCTestCase testInvocations]
и вернуть свои собственные объекты NSInvocation
в том порядке, в котором вы хотите выполнить тесты.
Что-то вроде:
+ (NSArray *)testInvocations
{
NSArray *selectorStrings = @[@"testFirstThing",
@"testSecondThing",
@"testAnotherThing",
@"testLastThing"];
NSMutableArray *result = [NSMutableArray array];
for (NSString *selectorString in selectorStrings) {
SEL selector = NSSelectorFromString(selectorString);
NSMethodSignature *methodSignature = [self instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = selector;
[result addObject:invocation];
}
return result;
}
Конечно, недостатком здесь является то, что вам нужно вручную добавить каждый тестовый метод к этому, вместо того, чтобы автоматически их подбирать. Есть несколько способов улучшить эту ситуацию. Если вам нужно всего лишь заказать некоторые из ваших методов тестирования, но не все из них, при переопределении +testInvocations
, вы можете вызвать супер, отфильтровать те методы, которые вы вручную заказывали, а затем отложить остальные на конец возвращаемого массива. Если вам нужно заказать все методы тестирования, вы все равно можете получить результат вызова супер и убедитесь, что все автоматически выбранные методы покрываются созданным вручную упорядоченным результатом. Если нет, вы можете утверждать, что это приводит к сбою, если вы забыли добавить какие-либо методы.
Я оставляю в стороне обсуждение вопроса о том, "правильно ли" писать тесты, которые должны выполняться в определенном порядке. Я думаю, что есть редкие сценарии, где это имеет смысл, но другие могут не согласиться.
Ответ 3
его упорядоченные по именам букв имен функций, не имеет значения, как вы заказываете его в своем коде.
например:.
-(void)testCFun(){};
-(void)testB2Fun(){};
-(void)testB1Fun(){};
фактический порядок выполнения:
- testB1Fun называется
- testB2Fun называется
- testCFun называется
Ответ 4
В дополнение к andrew-madsen ответ:
Я создал метод "умный" testInvocations
, который ищет селекторы, начинающиеся с "test", и сортирует их по алфавиту.
Таким образом, вам не нужно поддерживать NSArray
имен селектора отдельно.
#import <objc/runtime.h>
+ (NSArray <NSInvocation *> *)testInvocations
{
// Get the selectors of this class
unsigned int mc = 0;
Method *mlist = class_copyMethodList(self.class, &mc);
NSMutableArray *selectorNames = [NSMutableArray array];
for (int i = 0; i < mc; i++) {
NSString *name = [NSString stringWithFormat:@"%s", sel_getName(method_getName(mlist[i]))];
if (name.length > 4
&& [[name substringToIndex:4] isEqualToString:@"test"]) {
[selectorNames addObject:name];
}
}
// Sort them alphabetically
[selectorNames sortUsingComparator:^NSComparisonResult(NSString * _Nonnull sel1, NSString * _Nonnull sel2) {
return [sel1 compare:sel2];
}];
// Build the NSArray with NSInvocations
NSMutableArray *result = [NSMutableArray array];
for (NSString *selectorString in selectorNames) {
SEL selector = NSSelectorFromString(selectorString);
NSMethodSignature *methodSignature = [self instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = selector;
[result addObject:invocation];
}
return result;
}
Сноска: он считал плохую практику, которая заставляла ваши модульные тесты зависеть от друг друга.
Ответ 5
Например, вы можете просто переименовать файлы тестов в свой проект следующим образом:
SitchozrSDKSessionTest -> t001_SitchozrSDKSessionTest
SitchozrSDKMessageTest -> t002_SitchozrSDKMessageTest
Xcode обрабатывает файлы, используя алфавитный порядок.
Ответ 6
OP не упоминает, доступен ли CI или достаточно ответа в командной строке. В таких случаях мне нравилось использовать xctool [1]. Он имеет синтаксис для запуска тестов вплоть до одного тестового метода:
path/to/xctool.sh \
-workspace YourWorkspace.xcworkspace \
-scheme YourScheme \
test -only SomeTestTarget:SomeTestClass/testSomeMethod
Я сделаю это, чтобы убедиться, что мы тестируем элементы, которые мы считаем самыми легкими в первую очередь.
1. [] facebook/xctool: замена для Apple xcodebuild, которая упрощает сборку и тестирование приложений для iOS или OSX.;; https://github.com/facebook/xctool
Ответ 7
Так как Xcode 7, подклассы XCTestCase упорядочены по алфавиту. Если вы хотите убедиться, что сами методы тестирования также выполняются в алфавитном порядке, вы должны переопределить метод класса testInvocations
и вернуть вызовы, отсортированные по селектору.
@interface MyTestCase : XCTestCase
@end
@implementation MyTestCase
+ (NSArray<NSInvocation *> *) testInvocations
{
return [[super testInvocations] sortedArrayUsingComparator:^NSComparisonResult(NSInvocation *invocation1, NSInvocation *invocation2) {
return [NSStringFromSelector(invocation1.selector) compare:NSStringFromSelector(invocation2.selector)];
}];
}
- (void) test_01
{
}
- (void) test_02
{
}
@end