Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/122.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 如何使用KIF框架模拟位置服务_Ios_Objective C_Automated Tests_Ui Automation_Kif - Fatal编程技术网

Ios 如何使用KIF框架模拟位置服务

Ios 如何使用KIF框架模拟位置服务,ios,objective-c,automated-tests,ui-automation,kif,Ios,Objective C,Automated Tests,Ui Automation,Kif,我使用KIF框架()进行UI测试 我需要模拟定位服务 问题是位置服务在调用KIF方法之前启动。 所以现在嘲笑已经太迟了 任何建议都将不胜感激。在我的KIF目标中,我有一个BaseKIFSearchTestCase:KIFTestCase,我将覆盖类别中CLLocationManager的startUpdatingLocation 请注意,这是我做过的唯一一个类别覆盖,因为这通常不是一个好主意。但在测试目标中,我可以接受它 #import <CoreLocation/CoreLocation

我使用KIF框架()进行UI测试 我需要模拟定位服务

问题是位置服务在调用KIF方法之前启动。 所以现在嘲笑已经太迟了


任何建议都将不胜感激。

在我的KIF目标中,我有一个
BaseKIFSearchTestCase:KIFTestCase
,我将覆盖类别中CLLocationManager的startUpdatingLocation

请注意,这是我做过的唯一一个类别覆盖,因为这通常不是一个好主意。但在测试目标中,我可以接受它

#import <CoreLocation/CoreLocation.h>

#ifdef TARGET_IPHONE_SIMULATOR


@interface CLLocationManager (Simulator)
@end

@implementation CLLocationManager (Simulator)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

-(void)startUpdatingLocation 
{
    CLLocation *fakeLocation = [[CLLocation alloc] initWithLatitude:41.0096334 longitude:28.9651646];
    [self.delegate locationManager:self didUpdateLocations:@[fakeLocation]];
}
#pragma clang diagnostic pop

@end
#endif // TARGET_IPHONE_SIMULATOR



#import "BaseKIFSearchTestCase.h"

@interface BaseKIFSearchTestCase ()

@end

@implementation BaseKIFSearchTestCase
 //...

@end
  • 在测试目标方案中,选择新配置


  • 还有一个选择:


    可能是最好的:不需要更改代码。

    像往常一样,有几种方法可以做到这一点。关键不在于试图模拟现有的位置服务,而是要有一个在运行时可以访问的完全不同的模拟。我要描述的第一种方法基本上是构建自己的微型DI容器。第二种方法是获取通常无法访问的单例

    1) 重构代码,使其不直接使用LocationService。相反,将其封装在一个holder中(可以是一个简单的单例类)。然后,让您的持有者测试知道。其工作原理是您拥有类似LocationServiceHolder的东西,它具有:

    // Do some init for your self.realService and make this holder
    // a real singleton.
    
    + (LocationService*) locationService {
      return useMock ? self.mockService : self.realService;
    }
    
    - (void)useMock:(BOOL)useMock {
      self.useMock = useMock;
    }
    
    - (void)setMock:(LocationService*)mockService {
      self.mockService = mockService;
    }
    
    然后,每当您需要定位服务时,请致电

    [[LocationServiceHolder sharedService] locationService];  
    
    因此,当您进行测试时,您可以执行以下操作:

    - (void)beforeAll {
      id mock = OCClassMock([LocationService class]);
      [[LocationServiceHolder sharedService] useMock:YES]];
      [[LocationServiceHolder sharedService] setMock:mock]];
    }
    
    - (void)afterAll {
      [[LocationServiceHolder sharedService] useMock:NO]];
      [[LocationServiceHolder sharedService] setMock:nil]];      
    }
    
    #include "MockThirdPartyService.h"
    
    ...
    
    id mock = OCClassMock([ThirdPartyService class]);
    [ThirdPartyService setSharedInstance:mock];
    
    // set up your mock and do your testing here
    
    // Once you're done, clean up.
    [ThirdPartyService setSharedInstance:nil];
    // Now your singleton is no longer mocked and additional tests that
    // don't depend on mock behavior can continue running.
    
    当然,您可以在beforeach中这样做,并重写语义,使其比我在这里展示的基本版本要好一点

    2) 如果您使用的是第三方LocationService,它是一个无法修改的单例,它稍微有点棘手,但仍然可行。这里的技巧是使用一个类别覆盖现有的单例方法,并公开模拟而不是普通的单例。技巧中的技巧是,如果mock不存在,则能够将消息发送回原始单例

    假设你有一个叫第三方服务的单身汉。这是MockThirdPartyService.h:

    static ThirdPartyService *mockThirdPartyService;
    
    @interface ThirdPartyService (Testing)
    
    + (id)sharedInstance;
    + (void)setSharedInstance:(ThirdPartyService*)instance;
    + (id)mockInstance;
    
    @end
    
    这是MockThirdPartyService.m:

    #import "MockThirdPartyService.h"
    #import "NSObject+SupersequentImplementation.h"
    
    // Stubbing out ThirdPartyService singleton
    @implementation ThirdPartyService (Testing)
    
    +(id)sharedInstance {
        if ([self mockInstance] != nil) {
            return [self mockInstance];
        }
        // What the hell is going on here? See http://www.cocoawithlove.com/2008/03/supersequent-implementation.html
        IMP superSequentImp = [self getImplementationOf:_cmd after:impOfCallingMethod(self, _cmd)];
        id result = ((id(*)(id, SEL))superSequentImp)(self, _cmd);
        return result;
    }
    
    + (void)setSharedInstance:(ThirdPartyService *)instance {
        mockThirdPartyService = instance;
    }
    
    + (id)mockInstance {
        return mockThirdPartyService;
    }
    
    @end
    
    要使用,请执行以下操作:

    - (void)beforeAll {
      id mock = OCClassMock([LocationService class]);
      [[LocationServiceHolder sharedService] useMock:YES]];
      [[LocationServiceHolder sharedService] setMock:mock]];
    }
    
    - (void)afterAll {
      [[LocationServiceHolder sharedService] useMock:NO]];
      [[LocationServiceHolder sharedService] setMock:nil]];      
    }
    
    #include "MockThirdPartyService.h"
    
    ...
    
    id mock = OCClassMock([ThirdPartyService class]);
    [ThirdPartyService setSharedInstance:mock];
    
    // set up your mock and do your testing here
    
    // Once you're done, clean up.
    [ThirdPartyService setSharedInstance:nil];
    // Now your singleton is no longer mocked and additional tests that
    // don't depend on mock behavior can continue running.
    
    请参阅链接以了解超顺序实现的详细信息。Matt Gallagher的原创创意的疯狂道具。如果你需要,我也可以把文件寄给你


    结论:DI是一件好事。人们抱怨不得不重构和更改代码只是为了测试,但测试可能是高质量软件开发最重要的部分,而DI+ApplicationContext让事情变得简单多了。我们使用Typhone framework,但如果您正在进行任何级别的测试,即使您自己使用并采用DI+ApplicationContext模式,也非常值得。

    您可以提供任何示例代码来重现该问题吗?虽然您关于测试代码重构的结论是正确的,但您的答案中没有显示DI,当您切换服务的内部行为而不传入测试服务对象时。“DI和单身汉永远不会相处融洽。”维金戈塞贡多请重读我的答案。我特别指出,只有第一种方法是构建您自己的微型DI,而第二种方法是模拟您无法控制的单例,我从未声称这是DI。DI和单身人士相处得很好。DI允许您维护对象的任何类型的作用域(单例、弱单例等),并且仍然保持可测试性。以前从未听说过“微小DI”这个术语。一定错过了那个课程。@vikingosegundo如果我回答“只使用DI”,我认为这不会有什么帮助。如果OP有DI,这就不是问题了。这个问题的上下文意味着缺乏DI和对DI的认识,因此我给出了一个实现DI系统的基本示例,该系统没有很多特性,但为您提供了一个最小的抽象层。当事情超出你的控制范围时,还有一秒钟。不知道你要说什么,比如“在课堂上一定错过了”。直接批评我的答案。好吧,这里是我的批评者:没有理由让单身汉参与其中。但这正是你的答案所暗示的。为什么?我可以实例化
    cllocationmanager
    ?只要我需要它,我就可以把它传递到任何地方。我不需要围绕它的单例服务,它会打乱我的依赖关系。我只需要一个服务,我将它传递给任何需要它的对象。你的第三个选项看起来很简单,但似乎对我不起作用。我选择旧金山作为我的位置,但是当我运行测试时,我得到了一个失败的位置调用的警报。