Damodar
Damodar

Reputation: 707

LoginViewController writing cedar specs

I would like to write test-cases for the login screen. I'm writing test cases for login action.

Here is my code:

- (IBAction)loginAction:(id)sender {
    if ([[self.userNameTextField text] length] <=3 ||
        [[self.passwordTextField text] length] <=3 ) {
        UIAlertController *alert = [UIAlertController
                                    alertControllerWithTitle:@"Error"
                                    message:@"Username/Password \n length must be > 4 charecters"
                                    preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *action = [UIAlertAction
                                 actionWithTitle:@"OK"
                                 style:UIAlertActionStyleDefault
                                 handler:^(UIAlertAction * _Nonnull action) {
            [alert dismissViewControllerAnimated:true completion:nil];
        }];

        [alert addAction:action];
        [self presentViewController:alert animated:true completion:nil];
    } else {
        // success case
    }
}

Cedar Spec

describe(@"LoginViewController", ^{
    __block LoginViewController *subject;

    context(@"it should show the alert",^{
            beforeEach(^{
                subject = [LoginViewController instanceFromStoryboardForSpecs:subject identifier:@"login"];
                UITextField *txtUserName = [UITextField new];
                subject.userNameTextField = txtUserName;
                subject.userNameTextField.text = @"DA";
                [subject loginAction: nil];
            });

            it(@"it should be charecters < 3 ", ^{
                subject.userNameTextField.text.length should be_lte(3);
            });

            it(@"when be charecters < 3 ", ^{
                subject.presentedViewController should be_instance_of([UIAlertController class]);

                UIAlertController *alertController = (id)subject.presentedViewController;

                alertController.title should equal(@"Error"); // Important for proper styling
                alertController.message should equal(@"Username/Password \n length must be > 4 charecters");
                alertController.actions.count should equal(1);
                alertController.preferredStyle should equal(UIAlertControllerStyleAlert);

                UIAlertAction *cancelAction = alertController.actions.firstObject;
                cancelAction.title should equal(@"OK");


            });
        });

});

But its getting failed here subject.presentedViewController should be_instance_of([UIAlertController class]);

Can anyone help me to understand writing test cases? I went through with the Cedar WiKi, but I'm not able to understand how to write test cases for my case.

Upvotes: 3

Views: 104

Answers (1)

deekay
deekay

Reputation: 929

Fist question is, what does instanceFromStoryboardForSpecs do? Does it add your view controller to the UIWindow? If not, any call to presentViewController:animated:completion: will fail with an error (do you see any error in console?) and no alert will be shown.

Second, I'm confused by some parts of your code:

You instantiate LoginViewController from the storyboard, but need to create UITextField from code? I'd expect this to be loaded along the View Controller.

I'd split responsibilities:

// call validateUserName:password:, then either presentError: if needed
- (IBAction)loginAction:(id)sender

// login logic
- (void)loginWithUserName:(NSString *)userName password:(NSString *)password

// validation logic
- (BOOL)validateUserName:(NSString *)userName password:(NSString *)password

// use it every time you want to display error
- (BOOL)presentError:(NSString *)error

With that, you can Unit test (using simple XCTestCase) validation logic, if needed UI Test error-presentation logic (which can be reused to present different kinds of errors) or with BDD test user scenarios as a whole like you attempted.

Upvotes: 0

Related Questions