Classical vs Mockist testing

6 Comments

I’ve been looking for language to help articulate the difference between two styles of unit testing, and really like how Martin describes it in Mocks Aren’t Stubs.

Mocks use behaviour verification.
Classical uses state verification.

 

Classicist Mockist
Like working with real objects Prefer working with fake objects
State verification Behavior verification
Use mocks to test collaborations Use mocks all the time
Will hard code collaboration Will mock collaborations
TDD from middle out TDD from the outside in
Use ObjectMothers/Factories for test setup Will mock only what they need for test collaboration
Test tend to be more coarse grained – approaching more integration style tests Tests tend to be very fine grained – may miss integrations
Classists don’t couple tests to implementation Mockists do
Classists don’t like thinking about implementation when writing tests Mockists do
Don’t mind creating query methods to support testing Mockists typically don’t have to
Classists style can encourage Asking Not Telling design Mockists style encourages Tell Don’t Ask

Classical vs Mocking

I generally prefer the classical style to the mocking mostly because I don’t like thinking about implementation when writing a test.

I do however like having the mocking option in my back pocket, for those cases when a system is difficult to test, an object has a nasty setup, or I am refactoring legacy code.

The other piece of advice I recommend (my ex colleague Jonas Claesson turned me onto this) is to keep your calculations separate from your orchestrations.

If you can do this you get the best of both worlds. Calculations can be classically testing. Thin orchestrations mocked. Should keep both set easy to read, and easy to setup.

Here are some more summary notes from Martins article.

Article summary

Regular tests

  • state verification

Mock tests

  • behavior verification

Classical and Mockist Testing

  • Classical style uses real objects
  • Mocking style is to do everything with mocks
  • Classicists will use mocks for creating doubles

Choosing between the differences

  • If collaboration of objects is easy – classic is obvious choice
  • If collaboration between objects is awkward – mock
  • No big choice to be made here
  • Where things get more interesting is when we talk TDD

Driving TDD

  • Mockist will TDD there systems from the outside in
  • They will start with the GUI, describe their collaborations, and progressively marching deeper and deeper into the stack one layer at a time
  • Classical TDD doesn’t offer that kind of guidance
  • Whenever a classist requires something from a collaborator they simply hard code it
  • But classists take a slightly different approach – middle out
  • Here a classist will create an object for whatever domain they need and once they are working then you layer on the UI
  • Doing this you may never need to fake anything
  • Focuses the attention on the domain model, which helps keep the logic from leaking into the UI

Fixture Setup

  • Classic TDD needs to create SUT with all it’s collaborators
  • Mockists need only create SUT and mocks for immediate neighbours
  • Classics will do this using ObjectMothers – reuse whole objects
  • Mockists will say this is more work
  • Classists will say we do this once and reuse – you setup every time

Test Isolation

  • If you introduce a bug mocking, only those tests whose SUT contains the bug will fail
  • In classic, any tests of client objects also fail – ripple effect throughout system
  • Mocksts will say make finding bug harder to find
  • Classists say its usually pretty obvious – little bit more noise

Granularity

  • Classic tests can be more coarse grained than mockists as they test overall interaction of objects
  • In essence classic xunit tests are not just unit tests, but also mini-integration tests
  • Classists like this
  • Mocksts lose that quality – also run the risk that the mocks are incorrect masking inherent errors
  • Which ever way you go you need both – coarse and fine grained

Coupling Tests to implementation

  • Mockists couple their tests to implementation
  • Classiss don’t care about implementation – only the final state – not how it was arrived
  • With mock testing, writing the test makes you think about the implementation behavior
  • Mocksist like this – classists dont
  • Coupling implementation also interfere with refactoring, since implementaiton changes are more likely to break tests than classic testing

Design style

  • Classist are more comfortable creating query methods to support testing
  • Mockists don’t have to do that as much
  • Mocking also encourages Tell Don’t Ask
  • Classis can encourage Asking Not Telling – though in practice this is easily hadnleld
  • Mocking can also result in more classes (interfaces in various languages)
  • May not be as true now, but i remember many more interfaces and classes being ccreated than was necseary when mocking
  • Also DHH dislikes the designs mocking sometimes creates – mockists like it

Classic or mockist?

  • I have never seen a reason to go all mock
  • Mockists focus solely on implementation details of the SUT – never felt nature to me

Jonas

  • Don’t combine logic with orchestrations
  • Logic classis – classic mock
  • Orchestrations – simple as possible, no calcs, mockist

Links that help

http://www.martinfowler.com/articles/mocksArentStubs.html

http://googletesting.blogspot.se/2015/01/testing-on-toilet-change-detector-tests.html

How to setup OCMock XCode 4 iOS 5

4 Comments

This was harder than I would have liked. Here’s some notes on how to set it up.

Setup OCUnit on your existing project

These notes from Apple are pretty good about how to setup OCUnit on your XCode project.

The only difference was when I ran my unit tests (Product -> Test) I had to manually add my own unit test scheme.

Just hit the ‘Edit Scheme’ button, then hit the ‘+’ sign and click on your target unit tests and click the ‘Add button’.

You should now be able to run your tests against the simulator (not real phone) and see the failure.

Setup OCMock

Create a Libraries directory

Create a ‘Libraries’ directory in your project by right clicking on your project icon and selecting ‘Add Files to …’.

Click the ‘New folder’ button, type the name ‘Libraries’, and click ‘Add’ so your libraries directory is at the same level as your project and unit test target. When your done it should look something like this:

Note: Make sure you select your ‘Test’ target (grey) not the main ‘Project’ target blue. Else you will make all these lovely changes to the wrong config and nothing will work.

Copy in libOCMock.a

Download the libOCMock.a and stick it in the Libraries directory we just created.

Then add it to your project by dragging it into the Libraries directory we just added to your project.

When you do a window will pop up, make sure you select the ‘Copy items’ box at the top and click on your unit test target so it will be linked to OCMock.

Copy OCMock header files

Download the latest OCMock dmg file, located the OCMock directory inside containing the header files, and just like we did before, drag it into the Libraries directory.

Again select ‘Copy’ and click your unit test target and now your directory structure should look like this:

Configure Build Settings

We now need to tell our project how to find these header files we just added.

Click your blue project icon (click somewhere else and then click it again if it doesn’t immediately change) and click the ‘Build Settings’ tab for your project.

In the ‘Search Paths -> Library Search Paths’ section add $(SRCROOT)/Libraries in double quotes to Debug and Release sections. Once you do they will automatically convert to your local machine paths as shown below.

Note: As of XCode 4.3 the Library Search Paths may already be configured for you. The headers ones through aren’t.

And then do the exact same thing for the ‘Search Paths ->Header Search Path’ section (be sure to double click on it after and make sure both are set to ‘recursive’).

Note: As of XCode 4.3 you won’t see the word recursive here – just the box. Click it for both. If you don’t when you run your test you won’t be able to find the OCMock header files.

Next head down to the ‘Linking -> Other Link Flags’ section and add the ‘-all_load’ option.

If you don’t do this you’ll get a “Did you forget to add the -force_load linker flag?” error when you run your tests and you can read about why here.

Add a mock test case

We should now be good to go. Open you the default test case OCUnit created for you and add the following:

#import "UnitTests.h"
#import <OCMock/OCMock.h>

@implementation UnitTests

- (void)setUp
{
    [super setUp];
    
    // Set-up code here.
}

- (void)tearDown
{
    // Tear-down code here.
    
    [super tearDown];
}

- (void)testExample
{
    //STFail(@"Unit tests are not implemented yet in UnitTests");
}

- (void)testOCMockPass {
    id mock = [OCMockObject mockForClass:NSString.class];
    [[[mock stub] andReturn:@"mocktest"] lowercaseString];
    
    NSString *returnValue = [mock lowercaseString];
    STAssertEqualObjects(@"mocktest", returnValue, @"Should have returned the expected string.");
}

@end

If everything worked, you should be able to run your test now and see a nice failure in your log output window:

Links that help:

https://developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/UnitTesting/02-Setting_Up_Unit_Tests_in_a_Project/setting_up.html#//apple_ref/doc/uid/TP40002143-CH3-SW1

http://www.raywenderlich.com/3716/unit-testing-in-xcode-4-quick-start-guide

https://developer.apple.com/library/mac/#qa/qa1490/_index.html

http://ocmock.org/

%d bloggers like this: