无法模拟NSUserDefaults

时间:2014-10-09 13:18:13

标签: ios unit-testing mockito

我似乎无法模拟NSUserDefaults。我希望它能够发回我告诉它的数据,但它会在运行应用程序时发回存储在其中的数据。

我正在使用:

  • Specta
  • Expecta
  • OCMockito

我有以下测试:

describe(@"AppDelegate", ^{
    __block AppDelegate *appDelegate;

    beforeEach(^{
        appDelegate = [AppDelegate new];
    });

    afterEach(^{
        appDelegate = nil;
    });

    describe(@"application did finish launching with options", ^{
        beforeEach(^{
            [appDelegate application:nil didFinishLaunchingWithOptions:nil];

            NSUserDefaults *mockUserDefaults = mock([NSUserDefaults class]);
            [given([mockUserDefaults objectForKey:@"currentUser"]) willReturn:nil];

        });

        it(@"should have a login view controller as root view controller", ^{
            expect(appDelegate.window.rootViewController).to.beKindOf([ToALoginViewController class]);
        });
    });
});

所以上面的测试失败了,因为它实际上返回了currentUser的一些数据。我做错了什么?


继Ken Kuan所说的之后,我做了以下修改:

AppDelegate.h

@property (strong, nonatomic) NSUserDefaults *userDefaults;

- (void)setUserDefaults:(NSUserDefaults *)userDefaults;

AppDelegate.m

- (void)setUserDefaults:(NSUserDefaults *)userDefaults {
    _userDefaults = userDefaults;
}

AppDelegateSpec.m

beforeEach(^{
    [appDelegate application:nil didFinishLaunchingWithOptions:nil];

    NSUserDefaults *mockUserDefaults = mock([NSUserDefaults class]);
    [appDelegate setUserDefaults:mockUserDefaults];
    [given([mockUserDefaults objectForKey:@"currentUser"]) willReturn:nil];

});

但是,我仍然遇到同样的问题。

3 个答案:

答案 0 :(得分:3)

你可以模仿NSUserDefaults。我有这个代码,工作正常。 当你嘲笑NSUserdefaults时,请确保在测试用例结束时停止模拟,否则会导致您使用NSUserDefaults的其他地方出错。

id userDefaultsMock = OCMClassMock([NSUserDefaults class]);

OCMStub([userDefaultsMock standardUserDefaults]).andReturn(userDefaultsMock);

OCMStub([[userDefaultsMock standardUserDefaults] boolForKey:SHOWMYNAME]).andReturn(@"N");

[self.myObject  codewithuserdefaults];

 OCMVerify( [[NSUserDefaults standardUserDefaults] setValue:[OCMArg any] forKey:FIRSTNAME]);

[userDefaultsMock stopMocking];

答案 1 :(得分:1)

您必须在appDelegate中使用[NSUserDefaults standardUserDefaults],这是实际的用户默认值,而不是您的模拟。

解决方案是将用户默认设置为app delegate的属性,并在测试中使用mockUserDefaults进行设置。

另一个是调动[NSUserDefaults standardUserDefaults]以在测试中返回mockUserDefaults

答案 2 :(得分:1)

非常感谢Ken Kuan,我设法解决了我遇到的问题。以下是我如何实现它。

我在NSUserDefaults和setter函数中添加了AppDelegate属性。

我在self.userDefaults中将[NSUserDefaults standardUserDefaults]设置为application:willFinishLaunchingWithOptions:

然后我在rootViewController

中设置了application:didFinishLaunchingWithOptions:

<强> AppDelegate.h

@property (strong, nonatomic) NSUserDefaults *userDefaults;

- (void)setUserDefaults:(NSUserDefaults *)userDefaults;

<强> AppDelegate.m

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.userDefaults = [NSUserDefaults standardUserDefaults];

    return YES;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    NSData *encodedUserData = [self.userDefaults objectForKey:@"currentUser"];
    if (encodedUserData) {
        NSLog(@"Have current user");
        self.window.rootViewController = [ThermostatViewController new];
    } else {
        NSLog(@"No current user");
        self.window.rootViewController = [ToALoginViewController new];
    }

    [self.window makeKeyAndVisible];

    return YES;
}

- (void)setUserDefaults:(NSUserDefaults *)userDefaults {
    _userDefaults = userDefaults;
}

<强> AppDelegateSpec.m

describe(@"application will finish launching with options", ^{
    beforeEach(^{
        [appDelegate application:nil willFinishLaunchingWithOptions:nil];
    });

    context(@"application did finish launching with options and with no current user", ^{
        beforeEach(^{
            NSUserDefaults *mockUserDefaults = mock([NSUserDefaults class]);
            [appDelegate setUserDefaults:mockUserDefaults];
            [given([mockUserDefaults objectForKey:@"currentUser"]) willReturn:nil];

            [appDelegate application:nil didFinishLaunchingWithOptions:nil];
        });

        it(@"should have a login view controller as root view controller", ^{
            expect(appDelegate.window.rootViewController).to.beKindOf([ToALoginViewController class]);
        });
    });
});