Using Kiwi for BDD plus TDD

This past week I helped out one of our large project teams to knock down their bug backlog. This was code that I hadn’t really worked with before. It was delightful to work with a state of the art, new code base that fully utilized great architecture (VIPER), Uncle Bob’s clean code practices, extensive unit test coverage using Kiwi and OCUnit, and automated UI tests using KIF. Understanding the code was easy, and locating the correct spot in the code needing changes was very straight forward, a result of both good architecture and clean coding. I was even more delighted to find that the code needing changes was covered with Kiwi tests. I absolutely love working with Kiwi (debugging difficulties aside). It is so incredibly flexible, just about any type of testing can be accommodated (ATDD, BDD, TDD). I still have a lot of room for improvement in my skills at combining all of these though, and am hoping to take my skills in using it to higher levels.

One of the things that occurred to me (I apologize if I’m late to the party on this one) is that ATDD/BDD style tests can be facilitated by creating helper methods to abstract actions, states, and setting up state such as displaying a particular screen. For example, firstScreenIsDisplayed, acceptButtonWasTapped, userIsLoggedIn:, and displaySecondScreen. These can all probably be added to a Wireframe object (in VIPER architecture). Doing this would allow BDD tests to be clearly written such as:

describe(@"First screen", ^{
   __block Wireframe *wireframe;
   wireframe = [[Wireframe alloc] initForTesting];      
   [wireframe firstScreenIsDisplayed];
   context(@"user is NOT logged in", ^{
      [wireframe userIsLoggedIn:NO];
      context(@"Accept button is tapped", ^{
         it(@"issues a notification to log in", ^{
            [[[wireframe should] receive] displayLoginNotification];
            [wireframe acceptButtonWasTapped];
         });
      });
   });
   context(@"user is logged in", ^{
      [wireframe userIsLoggedIn:YES];
      context(@"Accept button is tapped", ^{
         it(@"goes to second screen", ^{
            [[[wireframe should] receive] displaySecondScreen];
            [wireframe acceptButtonWasTapped];
         });
      });
   });
});

This is just a rough draft of code, and probably syntactically incorrect. I’m going to build some tests using this approach, and will clean up the syntax and provide some more detail as I work it out. For example, readability is better without beforeEach/All (and left out above), so I want to see which happens if code is inserted without one of these (I’m hoping the same as beforeAll).

Leave a Reply