Как смоделировать свойство / внутреннюю ценность UIApplication? - PullRequest
0 голосов
/ 16 июля 2010

Я пишу юнит-тесты. И я не могу проверить одну функцию, потому что она вызывает keyWindow

UIWindow* window = [UIApplication sharedApplication].keyWindow;

И keyWindow возвращает nil (у меня нет окна). Но мне нужно вернуть все, кроме нуля.

Я использовал категорию для ручной установки значения keyWindow, но это не сработало

@interface UIApplication(UnitTest)
- (id)getKeyWindow;
@end

@implementation UIApplication(UnitTest)
- (id)getKeyWindow
{
    return [self keyWindow];
}
@end

// compiler error: lvalue required as left operand of assignment
[[UIApplication sharedApplication] getKeyWindow] = [[UIWindow alloc] init]; 

Что бы вы сделали на моем месте?

Ответы [ 3 ]

3 голосов
/ 08 ноября 2010

Apple не облегчает тестирование вашего кода, когда вы начинаете приближаться к классам фреймворка. Я нашел UIApplication особенно сложным из-за его одноэлементной природы и того факта, что среда создает экземпляр для вас во время установки. Решения таких проблем обычно зависят от того, как вы тестируете; Например, у вас есть действительный объект UIApplication в ваших тестах? Если вы используете OCUnit, то [UIApplication sharedApplication] может само вернуть nil, если вы не настроили свою цель тестирования для запуска в комплекте приложений (честно говоря, мне никогда не удавалось заставить это работать).

Один из вариантов - просто не проверять это. В большинстве случаев ваши взаимодействия с главным окном относительно просты, и тесты этого кода тестируют инфраструктуру UIKit так же, как и ваш собственный код. Это стилистическое решение, которое зависит от того, как вы структурировали свой код, насколько комфортно вы оставляете эту небольшую область непроверенной (или, что более уместно, не автоматизированной для тестирования) и насколько сложно будет писать тесты.

Если у вас есть код, относящийся к объекту UIWindow, который, по вашему мнению, вам необходимо протестировать, я бы посоветовал вам инкапсулировать функциональность таким образом, чтобы вы могли затем контролировать тестируемый объект. Вы можете сделать это, создав для своего приложения подкласс UIApplication, который возвращает объект UIWindow из пользовательского метода. Используйте этот метод в своем коде вместо непосредственного доступа к свойству окна и в своих тестах переопределите этот метод, чтобы возвращать все, что вам нравится.

0 голосов
/ 30 апреля 2019

Вы можете создать новый объект окна:

UIWindow * window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];

0 голосов
/ 03 августа 2013

Мне удалось смоделировать объект делегата и проверить определенные ожидания, такие как applicationDidBecomeActive, быстрым способом установщик делегата приложения.

id<UIApplicationDelegate> delegate = [MyCustomDelegate alloc] init];
UIApplication *app = [UIApplication alloc] init];
app.delegate = delegate;
//results in "There can only be one UIApplication" error

Вместо того, чтобы:

@implementation AppDelegateTests {

- (void)testAppDelegate {
     //perform swizzle on [UIApplication class] and method @selector(setDelegate:) with
     //[self class] and at the bottom @selector(swizzledSetDelegate:)

     id<UIApplicationDelegate> delegate = [MyCustomDelegate alloc] init];

     //Here's the magic, this line actually calls [UIApplication setDelegate] and not the swizzledSetDelegate method below. 
     //If you don't understand this, look up how swizzling works and hopefully you can wrap your head around it. The logic is quite mind-bendy.
     [self swizzledSetDelegate:delegate];

     //here set up your mock on the delegate and verify state of things
}

- (void)swizzledSetDelegate:(id<UIApplicationDelegate>)delegate {
     //do nothing
}

}

Обратите внимание, что этот пример совершенно не похож на то, что мне нужно было протестировать, вам нужно подумать о том, что вы хотите смоделировать, и если это вообще возможно.

...