Cedar's subjectAction

Frequently we find ourselves in that situation where avoiding code duplication in a spec looks virtually impossible. beforeEach blocks faking objects, stubbing methods and making calls, repeated for each context block as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
describe(@"View Lifecycle for Coffee", ^{
  __block Coffee *fakeCoffee;
  __block NSArray *ingredientsArray;
  __block CGFloat price;
  
  beforeEach(^{
      fakeCoffee = nice_fake_for([Coffee class]);
  });
  
  context(@"latte", ^{
      beforeEach(^{
          ingredientsArray = @[@"Coffee Beans", @"Water", @"Milk"];
          price = 3.50;
          
          fakeCoffee stub_method (@selector(ingredients)).and_return(ingredientsArray);
          fakeCoffee stub_method (@selector(price)).and_return(price);
          
          controller.coffee = fakeCoffee;
          [controller viewDidLoad];
          [controller viewWillAppear:YES];
      });
      
        it(@"should ...", ^{
            // ...
        });
  });

  context(@"espresso", ^{
      beforeEach(^{
          ingredientsArray = @[@"Coffee Beans", @"Water"];
          price = 1.75;
          
          fakeCoffee stub_method (@selector(ingredients)).and_return(ingredientsArray);
          fakeCoffee stub_method (@selector(price)).and_return(price);
          
          controller.coffee = fakeCoffee;
          [controller viewDidLoad];
          [controller viewWillAppear:YES];
      });
      
        it(@"should ...", ^{
            // ...
        });
  });
  
  context(@"americano", ^{
      beforeEach(^{
          ingredientsArray = @[@"Coffee Beans", @"Water", @"Water"];
          price = 2.00;
          
          fakeCoffee stub_method (@selector(ingredients)).and_return(ingredientsArray);
          fakeCoffee stub_method (@selector(price)).and_return(price);
          
          controller.coffee = fakeCoffee;
          [controller viewDidLoad];
          [controller viewWillAppear:YES];
      });
      
        it(@"should ...", ^{
            // ...
        });
  });
});

That’s because each context block tests a different setup (a coffee with different ingredientsArray and price), making us to recreate stubbed methods and redo calls:

1
2
3
4
5
6
fakeCoffee stub_method (@selector(ingredients)).and_return(ingredientsArray);
fakeCoffee stub_method (@selector(price)).and_return(price);

controller.coffee = fakeCoffee;
[controller viewDidLoad];
[controller viewWillAppear:YES];

But that’s where Cedar’s subjectAction enters. It extracts common setup to a single block to simplify the specs, which is perfect to avoid code duplication. subjectAction runs after all beforeEach blocks, and the example below shows it action to recreate the the same spec.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
describe(@"View Lifecycle for Coffee", ^{
  __block Coffee *fakeCoffee;
  __block NSArray *ingredientsArray;
  __block CGFloat price;
  
  beforeEach(^{
      fakeCoffee = nice_fake_for([Coffee class]);
  });
  
  subjectAction(^{
      fakeCoffee stub_method (@selector(ingredients)).and_return(ingredientsArray);
      fakeCoffee stub_method (@selector(price)).and_return(price);
      
      controller.coffee = fakeCoffee;
      [controller viewDidLoad];
      [controller viewWillAppear:YES];
  });
  
  context(@"latte", ^{
      beforeEach(^{
          ingredientsArray = @[@"Coffee Beans", @"Water", @"Milk"];
          price = 3.50;
      });
      
        it(@"should ...", ^{
            // ...
        });
  });

  context(@"espresso", ^{
      beforeEach(^{
          ingredientsArray = @[@"Coffee Beans", @"Water"];
          price = 1.75;
      });
      
        it(@"should ...", ^{
            // ...
        });
  });
  
  context(@"americano", ^{
      beforeEach(^{
          ingredientsArray = @[@"Coffee Beans", @"Water", @"Water"];
          price = 2.00;
      });
      
        it(@"should ...", ^{
            // ...
        });
  });
});

Copyright © 2014 - Otavio Cordeiro. Powered by Octopress