Tuesday, March 5, 2013

Git Course (Part I)

Ok, so you have problems with git, right? Welcome to the crash course that will explain to you the fundamentals and the way we use this thing.

Repositories

Git is a VCS, a Version Control System. VCS are trackers of the evolution of something, typically code.

VCS normally have a repository, and git is no different. A repository contains the history of changes made to the state of your code, in an internal format that is recognisable only by the VCS tool.

By using the repository, the VCS tool should able to take you to any state of the code in time, and track who did what at what point.




The firsts VCS had a centralised repository. This means that to sync work, the user would have to push his stuff to a central repository, and another user would have to fetch the things the user did from that repository, eventually pushing his things to the repository as well.

The users had no direct interaction between themselves. So, for example, if a developer wanted to share something with another, he would have to make it available to everybody else.

Git is different. Every code copy in git has its own repository. There's no central repository.  You maybe are asking yourself now "Wait a minute, what is github then?". The fact is, even if you setup a central repository, it's just the same repository with the same things everyone has in their own repository (except that it's probably a bare repository, but you don't need to know that). The fact that it's central is just a convention.

So, if a developer wants to send code directly to another, he may do so. He should also be able later to share it with everybody else. And they both should be able to send code to a third developer should they so wish, who should also sync his code with the "central" repository, without having any problems.

How git is able to perform this magic will be explained in the following section.

 How to create a git repository? Simple!!! Just go to any folder where you want to track stuff and execute

git init .

You will notice that in this folder was created a .git subdirectory. This .git is where, among other things, git will store your own repository.

Working Tree, Index, Commits

The Working Tree is the contents of your folder under version control except for the .git subfolder. It contains a state of the code as it is now.

Let's practice a little. Create a folder called git_tutorial. Inside this folder run git init . as set before.

Now execute:

echo "This is a cool file!!!" > a.txt

weasley:git_tutorial rafael$ git init .
Initialized empty Git repository in /Users/rafael/dev/git_tutorial/.git/
weasley:git_tutorial rafael$ echo "This is a cool file" > a.txt
weasley:git_tutorial rafael$ cat a.txt
This is a cool file
weasley:git_tutorial rafael$ ls
a.txt
weasley:git_tutorial rafael$ ls -lah
total 8
drwxr-xr-x 4 rafael staff 136B Mar 5 00:56 .
drwxr-xr-x 8 rafael staff 272B Mar 5 00:55 ..
drwxr-xr-x 10 rafael staff 340B Mar 5 00:56 .git
-rw-r--r-- 1 rafael staff 20B Mar 5 00:57 a.txt

If you notice, you know have a file a.txt and the .git subfolder. Now, run git status, and you should see the following:

weasley:git_tutorial rafael$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add ..." to include in what will be committed)
#
# a.txt
nothing added to commit but untracked files present (use "git add" to track)

What git status is telling you is that you have a untracked file called a.txt. Untracked means git won't care about this file and won't put these alterarations on the repository unless you tell git to track it. The way to do this is to add the changes you made to the index.

The index contains alterations on your working tree that are ready to go to the repository. To add some alteration to the index, you use the command git add (unless it's a file deletion, in which case you would have to use git rm, but I won't cover it in this tutorial because there's a shortcut that takes care of that). git add is recursive, so you could do a git add . to include all your alterations in the index.

Now execute git add a.txt and run a git status. You should now see this:

weasley:git_tutorial rafael$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached ..." to unstage)
#
# new file: a.txt

Now a.txt is not untracked anymore and ready to go to the repository. The way to put a set of alterations in a repository is by creating a commit with said alterations.

A commit is a bunch of alterations, a reference to the commits parent (the commit on witch this commit was based) and a SHA-1 code that identifies the commit.

The SHA-1 identifies the commit, and git will assume that two commits are the same if their SHA-1 code are the same. This is how git can know that commits in different repositories are actually the same commit.

Every commit, except the first one, has a parent. If you change the parent of a commit you change the SHA-1, so it becomes a different commits. This notion will be of extreme importance when we're talking about syncing your code with other people, so be sure to remember it.

To create a commit in our repository, just type git commit. Your favourite editor will appear (you can configure what it is by defining a shell variable called GIT_EDITOR) where you can type a message. After you type a message and close your editor, you just created a commit:

weasley:git_tutorial rafael$ git commit
[master 7113d77] Hello commit world!!!!
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt

If you run git status now:

weasley:git_tutorial rafael$ git status
# On branch master
nothing to commit (working directory clean)

It shows there is nothing to commit.  You can use git log to show the history of commits, and git log -p to see the history and a diff containing the changes.

Now let's learn a very useful shortcut. Put further stuff on a.txt

weasley:git_tutorial rafael$ echo "More content" >> a.txt
weasley:git_tutorial rafael$ git status
# On branch master
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
# modified: a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")



See that a.txt was modified, but the changes are not in the index yet, so if you generate a commit, these changes won't be on it.

You could add the changes by using git add, but that's boring.

If git already tracked previous changes on a file, you can add all changes on files that git already knows about by passing the -a flag to git commit.

So by executing git commit -a you add changes on files that were already in git to the index before creating the commit.

Be careful because this won't affect new files.

weasley:git_tutorial rafael$ git commit -a
[master ca93f24] Another commit.
 1 file changed, 1 insertion(+)

Also, take a look at git commit --amend. If you don't like a commit and wants to edit it, git commit --amend let's you remake that commit.

Next Steps

That's all for today. In the next part of this tutorial, I will tell you about the stash and I will tell you how you can change your commit history,

Till next time!

Monday, June 18, 2012

Presentation for JUG In Sao Jose dos Campos

Hello! Long time no see!

I'll make a presentation about Test Driven Development, with focus on Android development on the JUG Group in Sao Jose dos Campos this weekend.

These are the slides I will be presenting. It's in portuguese, but I'll upload a translation to English as soon as possible.

Here is the link to the presentation: http://www.slideshare.net/rafaeladson/android-testing-ptbr

Sunday, March 25, 2012

Intern-droid now on github.


Some time ago, I made intern-ios, my framework for iPhone and iPad applications, open on github.com. I also wanted to make intern-droid open, but as at the time I was developing for android (since I was focusing my efforts on kotoba v0.1 for iOS) and the intern-droid would not be platform independent, I postponed publishing it on github.

Today I began work in kotoba v0.2 for android (now the idea is to try to keep development of the two versions in parallel), and as promised, I did some modifications on intern-droid in order to release it on github.

So, if you want to check it out, please do so at https://github.com/rafaeladson/intern-droid

Wednesday, March 21, 2012

Starting iOS application in desired orientation

I have an storyboard based application, and I noticed that when I started the application on iPhone, it always started on landscape, then after the application started it would then autorotate to portrait if the cell phone was in portrait mode. When I ran the application on the iPad instead, it would always start on portrait. So I searched the web to find how to fix it, and by examining some posts about it and tinkering a little with Xcode, I found the following solution to the problem: Inside the application target, there's a -Info.plist file. This file contains some configuration properties of the application. Among those, there's a "Supported interface orientations" property for both iPhone and iPad. My configuration for the iphone was the following: 

Item 0 Landscape (left home button)
Item 1 Landscape (right home button)
Item 2 Portrait (button home button)
I found out that the order of these items matter. The application will always start on the first orientation in this list. I was then able to solve my problem by dragging the Portrait (button home button) to the top of the list so that it turned into Item 0. Then, after I started my application again, it started in Portrait by default. Just posting in the case that someone has the same problem.

Sunday, March 18, 2012

TDD on iOS - Part I.


I want to make a blog post about how to practice TDD on iPhone. My approach in this tutorial will be:

  • Build the prototype (the screen of the application)
  • For every functionality, first we will create the tests, then we will implement the code that make the tests pass.

What I won’t do in this post:
  • I won’t make a full application. I’ll try to illustrate how someone could use TDD to make a full application as an example, but I won’t try to do one.
  • I won’t worry about error conditions, like cases where a field in a form is obligatory, but the user decides not to fill it. However, from the examples that I do give, the approach to test these kind of conditions will hopefully become trivial.


The Prototype
I want to make for this example a birthday reminder application. It’s a very simple application that have the following user cases:
  • A user should be able to save the birthday date from his friends.
  • The user should be able to see all the birthday dates that he saved.

Based on these user cases, I created this prototype:



So, the user will see the list of people in a tableView. Every person will have on the cell his name and his birthday listed. The user will also be able to register new friends by clicking on the + button, typing the details for the friend, and clicking on the Save button.
The new registered friend should then appear on the table view.

I won’t save the data on the database for this example. It’s possible to do TDD in database oriented applications, and if you  want to know how I suggest you take a look at https://github.com/rafaeladson/intern-objc/blob/master/InternIOSTest/DataManagerBaseTest.m. Instead, I’ll use an array shared in the application delegate to simplify the example.

I’ll have two controllers: PeopleTableViewController, which will list the people and their respective birthdays, and NewPersonViewController, which will manage the view where the user can save new People.

The first test
First I’ll make the setup of the first test. In this setup, I’ll try to get the view from the storyboard. I’ll use the same approach I did on  http://yetanotherdevelopersblog.blogspot.com.br/2012/03/how-to-test-storyboard-ios-view.html

Here’s the setup of the first test:

#import "NewPersonViewControllerTest.h"
#import "NewPersonViewController.h"

@interface NewPersonViewControllerTest()

@property (strong, nonatomic) NewPersonViewController *controller;

@end

@implementation NewPersonViewControllerTest

@synthesize controller = _controller;


-(void) setUp {
   
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];
    self.controller = [storyboard instantiateViewControllerWithIdentifier:@"NewPersonViewController"];
    [self.controller performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES];

}

-(void) testPreConditions {
    STAssertNotNil(self.controller, nil);
}


@end

When I try to run this test, it will fail with the following message:

error: testPreConditions (NewPersonViewControllerTest) failed: Storyboard () doesn't contain a view controller with identifier 'NewPersonViewController'

To fix it, I have to edit my storyboard file to define that this controller has the identifier called new person view controller, in the same way I explained in my storyboard testing tutorial. After I do this, the test passes.

Now, for the first test, I’ll fill in the two fields and click on save. If all goes well, the array should end up with one element.


-(void) testSaveANewPerson {
    [self.controller.nameTextField performSelectorOnMainThread:@selector(setText:) withObject:@"Mike" waitUntilDone:YES];
    [self.controller.birthdayTextField performSelectorOnMainThread:@selector(setText:) withObject:@"October 10th" waitUntilDone:YES];
   
    [self.controller onSaveAction:nil];
   
    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    NSArray *people = delegate.people;
    int peopleCount = [people count];
    STAssertEquals(1, peopleCount, nil);
   
    Person *personInArray = [people objectAtIndex:0];
    STAssertEqualObjects(@"Mike", personInArray.name, nil);
    STAssertEqualObjects(@"October 10th", personInArray.birthday, nil);
   
}

Also, when I was making this test, I created the infrastructure for the test to compile, defining the outlets for nameTextField and birthdayTextField, the IBAction for onSaveAction and the people array on the application  delegate. However, I haven’t done any implementation yet. When I try to run this test, I get the following errors:

file://localhost/Users/rafael/dev/learning/examples/TDD/TDDTests/NewPersonViewControllerTest.m: error: testSaveANewPerson (NewPersonViewControllerTest) failed: '1' should be equal to '0':

file://localhost/Users/rafael/dev/learning/examples/TDD/TDDTests/NewPersonViewControllerTest.m: error: testSaveANewPerson (NewPersonViewControllerTest) failed: '<18a57906>' should be equal to '<00000000>':

file://localhost/Users/rafael/dev/learning/examples/TDD/TDDTests/NewPersonViewControllerTest.m: error: testSaveANewPerson (NewPersonViewControllerTest) failed: '<28a57906>' should be equal to '<00000000>':


Ok, so now I now that my tests are failing because the array should have 0 elements and it’s not initialized, so I have trash in the variables. Let’s try to implement by first modifying the application delegate to initialize the people’s array:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.people = [[NSMutableArray alloc] init ];
    return YES;
}

Now, let’s change the onSaveAction to save the person:

- (IBAction)onSaveAction:(id)sender {
    Person *person = [[Person alloc] init];
    person.name = [self.nameTextField text];
    person.birthday = [self.birthdayTextField text];
   
    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    [delegate.people addObject:person];
    [self.navigationController popViewControllerAnimated:YES];
}



Now, the tests pass.

In the last line, I did a popViewController to assure that after saving a new person I would automatically go back to the previous screen. If I wanted to test this behaviour, I would propably do it in an acceptance test rather than in a integration test like this one.



Coming up next

This is getting long, so I’ll break in several posts. In the next post I’ll tell you how you can implement the tableview part of the test and how to test segue behaviour.








You can check the code for this post at github. I'll change this code when I implement the following blog posts, so it's possible that the code will be a little different than what you saw here, but the principle will be the same.


Till then!
About Intern

Intern is now open source.

I’ve been busy, so I neglected to post this in quite some time, but I have made Intern open source.

Intern was created because I did not want to repeat some custom implementations in every application that I do for iOS. Probably every table view that I program will have the default implementation to delete an item, I’ll probably aways create the core data document which will hold my objects in the same way, and I want some default classes that make it easier to write my tests. Instead of doing that every time, it seemed more appropriate to create a project for it (even though I still have only one project for iPhone).

It’s quite possible that in the projects I develop to make posts in this blog, I’ll use intern to make my job easier if it doesn’t get in the way to illustrate the points I want to make.

Also, there’s an Intern version for java and android applications too. It will also be made open source when I close kotoba’s version 0.1 for iphone and start to develop version 0.2 to both iPhone and Android. I don’t make it open source now because I have to work a little for it to be ready.

Anyway, you can check intern at  https://github.com/rafaeladson/intern-objc


Sunday, March 4, 2012

How to test storyboard iOS view Controllers.

I’m building an IOS 5.0 storyboard based application. As I began to write my controllers, I wanted a way to test things like:

1. You type this on this text box
2. You click on save.
3. Assert that the thing is saved on database.

I used to solve this kind of problem by instantiating testFields manually during the test and setting on the controller. However, when I tried using UITextView, I found out that I couldn’t instantiate an UITextView manually because  it should be instantiated on the main thread.

So I began to google for it, and at first it wasn’t promising. People kept telling me that to do that sort of testing I would have to use UIAutomation, but I was hoping to use UIAutomation to make tests that were more like system testing, and I really wanted to do some white box testing, like checking the database, which would be difficult or even impossible with UIAutomation.

So after a few more searches, I came across this blog: http://blog.carbonfive.com/2010/03/10/testing-view-controllers/. And it is good, because now I’ve seen how I can get a viewController with all the views already instantiated, which both simplified my tests and gave me hope that I could solve my problem.

However, this is how the author of the article instantiate a view controller:

    TestableSimpleViewController *viewController = [[TestableSimpleViewController alloc] initWithNibName:@"TestableSimpleViewController" bundle:nil];

This was bad for me, because in storyboards applications, the .nib file is encapsulated inside a .storyboard file, and I didn’t want to break this encapsulation nor did I want to recreate the nib files. So it was time to hit the apple documentation (which, fortunately, is very good). In this post I will describe the solution I came up with.

First, the project
I’ve came up with a one screen view project as a demonstration. It is a sample application where the user has two text boxes, and he types something on the first one, hit the button that says Copy Text, and the text is reproduced on the second text field.

This is the screenshot of my storyboard.



The first thing I did was create another target, called UnitTests, where I would put my tests. I then setup this second target to use GHUnit as explained in the GHUnit documentation located at http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_install_ios_4.html. (The gunit documentation is a little outdated, but it is not difficult to adapt it to ios 5. You can use OCUnit also, but this tutorial will be demonstrated using GUnit).

Also make sure that both the iPhone.storyboard and all classes involved on the testing are also included in the UnitTests target. The way you can do this is clicking on each class, selecting the File Inspector, and making sure in the Target Membership view that UnitTests is selected for each file.

The last thing I gotta show about the project is the code. This are the contents of ViewController.h (my only ViewController class):


@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *originalTextField;
@property (weak, nonatomic) IBOutlet UITextField *resultTextField;
- (IBAction)copyText:(id)sender;

@end

I have to outlets, one for each textField, and also an IBAction. The important code is the copyText function, which is replaced below:

- (IBAction)copyText:(id)sender {
    self.resultTextField.text = self.originalTextField.text;
}

So now that I have everything in place, let’s test this.

Testing Setup
So, for this demonstration I will make one simple test that checks that if I click on the save text button, the second text box will be filled with the contents of the first box.

The first thing I gotta do is get the controller reference. To do this, I use the UIStoryboard object.

@interface ViewControllerTest : GHTestCase

@property (strong, nonatomic) ViewController *controller;

@end


@implementation ViewControllerTest

@synthesize controller = _controller;


-(void) setUp {
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];
    self.controller = [storyboard instantiateViewControllerWithIdentifier:@"Bob"];
    [self.controller performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES];
   
}

-(void) testCopyText {
   
   
}

@end


In the setUp method I instantiated a storyboard with a name that is equal to the name of the storyboard bundle without the .storyboard extension.

Second, I instantiated a controller from the storyboard. In order to do this, I had to give the name of the controller (in this case, Bob).

So how do the storyboard now which of my controllers has the name Bob. Well, I had to tell it, of course. The way I do this is:
1. edit the storyboard file
2. select the view controller that I want to name
click on the attributes Inspector
3. On the View Controller section, that is a TextField labeled Identifier. Type Bob there.




The last thing I had to do is call a load view in the main thread. And now my controller is ready to be used.


Writing the test

Now, what’s missing is implementing the testCopyText function.

-(void) testCopyText {
    [self.controller.originalTextField performSelectorOnMainThread:@selector(setText:) withObject:@"foo" waitUntilDone:YES];
    [self.controller copyText:nil];
    GHAssertEqualStrings(@"foo", self.controller.resultTextField.text, nil);
   
}

The first I thing I did was set the text on the originalTextField. However, as this text can only be set on the main thread (since I’m interfering with the view), I used the prformSelectorOnMainThread method to do it.

The rest is pretty straightforward. I call the copyText: method and verify that the second resultTextField was set correctly.

Source code
You can find the source code at github.