C# TDD Videos - You asked for them 161

Posted by Brett Schuchert Thu, 15 Apr 2010 04:29:00 GMT

Several people asked for them, so here is a series of 4 videos. The first series on the RPN calculator in Java was a bit rough, these are even rougher.

Even so, hope you find them valuable.

Shunting Yard Algorithm in C# Video Album

Comments and feedback welcome.

Getting Started in Objective-C with XCode 227

Posted by Brett Schuchert Fri, 12 Feb 2010 05:07:00 GMT

As I mentioned in a previous previous blog, I wrote up some notes on getting started practicing TDD using XCode and Objective-C. I’m intending to write a bit more, so the top level of the overall tutorial path starts here: A First Objective-C Project

If you just want to see the details of getting started: XCodeProjectSetup

The biggest change from the previous blog is an embedded video demonstration. If you’re just interested in the video: Getting started

Want to seem more? Something in particular? Let me know.

Comments welcome.

Rudimentary TDD with XCode and Objective-C 154

Posted by Brett Schuchert Tue, 09 Feb 2010 00:24:00 GMT

We have a potential client that would like us to teach a TDD class in several languages simultaneously:
  • Java
  • JavaScript
  • C++
  • Objective-C

In turns out that for what we teach in our TDD class, Java, C++ and Objective-C can be taught at the same time fairly easily. JavaScript, however, not so much. It is a very different (great, but different) beast.

In fact I could add C# to the mix as well, but I’d have to leave out LINQ and Lambdas.

In any case, to teach a class like that, I prefer to have the students do a bit of preliminary work so that they know how to set up their environments. When there’s just one language, I do this on the fly. But when there’s 3, I don’t want 2/3rds of the class waiting while 1/3rd learns how to create a basic project supporting TDD.

The last time I used Objective-C was in 1991, so I needed to figure out how to do this in the environment it is most likely to be used. This turned out to be a bit more work than I expected. I found it all using the good old google-machine, but in pieces. Since there really wasn’t a great (in my opinion) one-stop place that clearly laid out all of the steps, I created one.

This is in a early state. If you have comments, I’d appreciate them. I’ll probably add more. (If you’d like to see more, let me know that as well.) At this point, the basics are pretty much there, so I don’t actually need to do anything else. However, I have taken this just a bit further: This is the top-level of the tutorial

Again, comments and suggestions welcome.

CppUTest Recent Experiences 163

Posted by Brett Schuchert Thu, 04 Feb 2010 19:42:00 GMT

Background

Mid last year I ported several exercises from Java to C++. At that time, I used CppUTest 1.x and Boost 1.38. Finally, half a year later, it was time to actually brush the dust off those examples and make sure they still work.

They didn’t. Bit rot. Or user error. Not sure which.

Bit rot: bits decay to the point where things start failing. Compiled programs do have a half-life.

User Error: maybe things were not checked in as clean as I remember. Though I suspect they were, I don’t really have any evidence to prove it, so I have to leave that option available.

To add to the mix, I decided to upgrade to CppUTest 2.x and to the latest version of the Boost library (1.41). I think that broke many several things. But the fixes were simple, once I figured out what I needed to do.

The Fixes

What follows are the three things I needed to do to get CppUTest 2.0, Boost and those exercises playing nicely together.

Header File Include Order

First, I used to have the header file for the CppUTest Test Harness, included first. It seems logical, but it caused all sorts of problems with CppUTest 2. That header file, includes a file, that ultimately includes something that uses macros to redefine new and delete. This is done so the testing framework can do simple memory tracking, which lets you know if your unit tests contains memory leaks.

I like this feature. Sure, it’s simple and light-weight, but it coves a lot of ground for a little hassle. The hassle? Include that header file last, instead of first. Problem Solved. Well at least the code compiles without hundreds of errors.

Boost Shared Pointer

Rather than hold pointers directly, I used the boost shared pointer class for a light-weight way to manage memory allocation. This is something I would do on a real project as well.

Somehow, the updated memory tracking in CppUTest 2.0 found something I had missed when using CppUTest 1.0.

I need to be able to control the date, so I have a simple date factory. By default, the date factory, when asked for the current date, returns the current date. Several unit tests want to simulate different dates. E.g., check out a book on one day, return it 14 days later. To do that, I manipulate the date factory (a form of dependency injection). This works fine, but by default the date factory is allocated using new.

When I replaced the existing date factory, I was not resetting it after the test. It turns out that this did not break anything because I was “lucky”. (Actually unlucky, I like things to fail fast.) CppUTest caught this in the form of not deallocating memory correctly:

  • I want to replace behavior
  • To do so I used polymorphism
  • Polymorphism in C++ requires virtual methods (please don’t correct me by suggesting that overloading is polymorphism, that is an opinion with which I strongly disagree)
  • Methods are only virtually dispatched via references or pointers
  • References cannot be changed, so I must use a pointer if I want a substitutable factory, which I wanted
  • Pointers suggest dynamic memory allocation

To fix this, I updated the setup method to store the original date factory in an attribute and then I updated the teardown method to restore the original date factory from that attribute. That I missed this suggests that my test suite is not adequate. I did not fix this problem for no good reason other than I was porting existing tests, so I left it as is. For the context it will not cause a problem. Pragmatic or lazy? You decide.

One Time Allocation

Here is a simple utility that uses Boost dates and regex:
ptime DateUtil::dateFromString(const string &dateString) {
  boost::regex e("^(\\d{1,2})/(\\d{1,2})/(\\d{4})$");
  string replace("\\3/\\1/\\2");
  string isoDate = boost::regex_replace(dateString, e, replace, boost::match_default | boost::format_sed);
  return ptime(date(from_string(isoDate)));
}

Now this is somewhat simplistic code. So be it, it serves the purposes of the exercise. I can think of ways to fix this, but there’s an underling issue that exists if you use the regex library from Boost.

When you use the library, it allocates (in this example) 10 blocks of memory. If you read the documentation (I did), it’s making space for its internal state machine for regex evaluation. This is done once and then kept around.

So what’s the problem? Well, when I run my tests, the first test that happens to exercise this block of code reports some memory allocation issues:

c:\projects\cppppp\dependencyinversionprinciple\dependencyinversionprinciple\pat
rongatewaytest.cpp:34: error: Failure in TEST(PatronGateway, AddAFew)
        Memory leak(s) found.
Leak size: 1120 Allocated at: <unknown> and line: 0. Type: "new" Content: "
?"
Leak size: 16 Allocated at: <unknown> and line: 0. Type: "new" Content: ?a"
Leak size: 20 Allocated at: <unknown> and line: 0. Type: "new" Content: "êà4"
Leak size: 52 Allocated at: <unknown> and line: 0. Type: "new" Content: ä4"
Leak size: 4096 Allocated at: <unknown> and line: 0. Type: "new" Content: ""
Leak size: 52 Allocated at: <unknown> and line: 0. Type: "new" Content: "êâ4"
Leak size: 20 Allocated at: <unknown> and line: 0. Type: "new" Content: ~4"
Leak size: 32 Allocated at: <unknown> and line: 0. Type: "new" Content: "?à4"
Leak size: 32 Allocated at: <unknown> and line: 0. Type: "new" Content: "h~4"
Leak size: 80 Allocated at: <unknown> and line: 0. Type: "new" Content: "êä4"
Total number of leaks:  10

This is a false positive. This is a one-time allocation and a side-effect of C++ memory allocation and static initialization.

There is a way to “fix” this. You use a command line option, -r, to tell the command line test runner to run the tests twice. If the allocation problem happens the first time but not the second time, then the tests are “OK”.

I didn’t want to do this.

  • The tests do take some time to run (30 seconds maybe, but still that doubles the time)
  • The output is ugly
  • It’s off topic for what the exercise is trying to accomplish
I tried a few different options but ultimately I went with simply calling that method before using the command line test runner. So I changed my main from:
#include <CppUTest/CommandLineTestRunner.h>

int main(int argc, char **argv) {
  return CommandLineTestRunner::RunAllTests(argc, argv);
}
To this:
#include "DateUtil.h"
#include <CppUTest/CommandLineTestRunner.h>

/** ************************************************************
    The boost regex library allocates several blocks of memory
    for its internal state machine. That memory is listed as a 
    memory leak in the first test that happens to use code that
    uses the boost regext library. To avoid having to run the
    tests twice using the -r option, we instead simply force
    this one-time allocation before starting test execution.
    *********************************************************** **/

void forceBoostRegexOneTimeAllocation() {
  DateUtil::dateFromString("1/1/1980");
}

int main(int argc, char **argv) {
  forceBoostRegexOneTimeAllocation();
  return CommandLineTestRunner::RunAllTests(argc, argv);
}

Since this one-time allocation happens before any of the tests run, it is no longer reported as a problem by CppUTest.

Before I introduced this “fix”, I spent quite a bit of time to verify that each of the 10 allocations were done by one of the three lines dealing with regex code in my DateUtil class. I used a conditional breakpoint and looked at the stack trace. (I know, using the debugger is considered a code smell, but not all smells are bad.)

Conclusion

I still like CppUTest. I’ve used a few C++ unit testing tools but there are several I have not tried. I don’t have enough face-time with C++ for this to be an issue. I am not terribly comfortable with the order of includes sensitivity. I’m not sure if that would scale.

I do appreciate the assistance with memory checking, though dealing with false positives can be a bit of a hassle. There was another technique, that of expressing the number of allocations. But in this case, that simply deferred the reporting of memory leaks to after test execution. In any case, I do like this. I’m not sure how well it would scale so it leaves me a bit uneasy.

If you happen to be using these tools, hope this helps. If not, and you are using C++, what can you say about your experiences with using this or other unit testing tools?

Makefiles, what are you doing with them? 147

Posted by Brett Schuchert Wed, 27 Jan 2010 15:02:00 GMT

Half a year ago, I took some exercises written in Java and ported them to C++. At that time I punted on writing makefiles and decided to reply on an IDE (Visual Studio). That worked fine but recently we almost had a customer that was going to take this class and, bless their heart, use the command line.

Turns out they are going to use visual studio, so I can shelve this, but I figured I’d get feedback on what I have created for a makefile. Since I have not written a makefile for 12ish years, I figured things have changed. They have, but I suspect they have even more.

What follows are my environment assumptions and the makefile. I’d appreciate any suggestions.

FWIW, I wrote this makefile because it turns out fully building all files takes longer than just building what has changed. I know this seems obvious, but it is not always the case with small projects like this. There is some point where the project becomes big enough to warrant dependency-based building. The exercise meets that criteria. Sometimes the time taken to evaluate build changes is enough overhead that it can be safely removed in lieu of a full build. This is a consideration when creating custom build files for legacy-based refactoring efforts.

Environment
  • Generic Unix
  • zsh – though any shell will do really
  • g++
  • gnu make
  • This source base will still be used in Visual Studio but not this makefile
  • CppUTest used for unit testing tool
  • Provide default location for CppUTest, which can be overridden by environment variable
  • Boost library installed in /usr/local/include and /usr/local/lib (reasonable assumption or should I go ahead and allow this to be configured easily in the makefile – what’s your opinion?)
  • Project is small enough that all headers and source files are in the same directory (should I build the .o’s and put them in another directory to hide them?)
Current Makefile
ifdef CPP_U_TEST
    CppUTest_base = $(CPP_U_TEST)
else
    CppUTest_base = $(wildcard ~/src/cpputest)
endif

LIB_DIRS = -L$(CppUTest_base)/lib
INC_DIRS = -I$(CppUTest_base)/include
LIBS = -lCppUTest -lboost_date_time -lboost_regex -lboost_thread
CFLAGS = -g $(INC_DIRS) 
LDFLAGS = $(LIB_DIRS) $(LIBS)
CC = g++
SRCS := $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
OUT = run_all_tests
DEP_DIR = dependencies

all: $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $(OUT) $(OBJS)

clean:
    @rm -rf $(DEP_DIR) $(OBJS) $(OUT)

-include $(SRCS:.cpp=$(dep_dir)/.d)

.cpp.o:
    @if [ ! -d $(DEP_DIR) ] ; then mkdir $(DEP_DIR) ; fi
    $(CC) -c $(CFLAGS) -o $@ $< 
    @$(CC) -MM $(CFLAGS) $*.cpp > $(DEP_DIR)/$*.d

C++ Bowling Kata Result 65

Posted by Brett Schuchert Tue, 27 Oct 2009 20:37:00 GMT

I’m teaching a TDD and Refactoring class this week using C++. Since I had not recently wrote the bowling kata in C++, I figured it was about time to do it again.

Unlike the previous Scala version, this one only addresses the happy-path. I do not consider throwing too many balls or scoring too many pins in any frame. However, having just written this in Scala, I’m sure I could do something similar in C++.

I just switched to CppUTest 2.0 and something I noticed is that if you use <vector> or other std-based classes, you need to make sure to include those first before including <CppUTest/TestHarness.h>. This is because CppUTest overloads new and delete, which causes havoc with the std-based classes. No big deal, I just made sure to include that file as the last header (rather than the first, which is what I used to do).

Here are the various files:

RunAllTests.cpp

1
2
3
4
5
#include <CppUTest/CommandLineTestRunner.h>

int main(int argc, char** argv) {
  CommandLineTestRunner::RunAllTests(argc, argv);
}

BowlingScoreCardTest.cpp

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "BowlingScoreCard.h"

#include <CppUTest/TestHarness.h>

TEST_GROUP(BowlingScoreCard) {
  BowlingScoreCard* card;

  TEST_SETUP() {
    card = new BowlingScoreCard;
  }

  TEST_TEARDOWN() {
    delete card;
  }

  void throwOne(int value) {
      card->recordThrow(value);
  }

  void throwMany(int rolls, int value) {
    for(int i = 0; i < rolls; ++i)
      throwOne(value);
  }

  void confirmScoreIs(int expected) {
    LONGS_EQUAL(expected, card->score());
  }
};

TEST(BowlingScoreCard, noRollsGame) {
  confirmScoreIs(0);
}

TEST(BowlingScoreCard, throwAll0s) {
  throwMany(20, 0);
  confirmScoreIs(0);
}

TEST(BowlingScoreCard, throwAll1s) {
  throwMany(20, 1);
  confirmScoreIs(20);
}

TEST(BowlingScoreCard, throwOneSpare) {
  throwOne(7);
  throwOne(3);
  throwOne(6);
  throwMany(17, 0);
  confirmScoreIs(22);
}

TEST(BowlingScoreCard, all5sthrown) {
  throwMany(21, 5);
  confirmScoreIs(150);
}

TEST(BowlingScoreCard, throwOneStrike) {
  throwOne(10);
  throwOne(4);
  throwOne(3);
  confirmScoreIs(24);
}

TEST(BowlingScoreCard, perfectGame) {
  throwMany(12, 10);
  confirmScoreIs(300);
}

TEST(BowlingScoreCard, dutch200StrikeSpare) {
  for(int i = 0; i < 10; ++i) {
    throwOne(10);
    throwMany(2, 5);
  }
  throwOne(10);
  confirmScoreIs(200);
}

TEST(BowlingScoreCard, dutch200SpareStrike) {
  for(int i = 0; i < 10; ++i) {
    throwMany(2, 5);
    throwOne(10);
  }
  throwMany(2, 5);
  confirmScoreIs(200);
}

BowlingScoreCard.h

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
#pragma once

#ifndef BOWLING_SCORE_CARD_H
#define BOWLING_SCORE_CARD_H

#include <vector>
using namespace std;

class BowlingScoreCard
{
public:
  enum { Frames = 10, Mark = 10 };

  BowlingScoreCard();
  virtual ~BowlingScoreCard();
  int score();
  void recordThrow(int roll);

private:
  typedef vector<int> v_int;
  typedef v_int::size_type size_type;

  int scoreFrameAt(size_type index);
  int nextTwoRollsSummed(size_type index);
  int scoreAt(size_type index);
  int frameSizeAt(size_type index);
  bool isStrikeAt(size_type index);
  bool isSpareAt(size_type index);
  int scoreStrikeAt(size_type index);
  int scoreSpareAt(size_type index);

  v_int rolls;
};

#endif

BowlingScoreCard.cpp

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
#include "BowlingScoreCard.h"

BowlingScoreCard::BowlingScoreCard() {
}

BowlingScoreCard::~BowlingScoreCard() {
}

int BowlingScoreCard::score() {
  int score = 0;

  for(int index = 0, frame = 0; frame < Frames; ++frame) {
    score += scoreFrameAt(index);
    index += frameSizeAt(index);
  }

  return score;
}

int BowlingScoreCard::scoreFrameAt(size_type index) {
  if(isStrikeAt(index))
    return scoreStrikeAt(index);
  else if(isSpareAt(index))
    return scoreSpareAt(index);

  return nextTwoRollsSummed(index);
}

bool BowlingScoreCard::isStrikeAt(size_type index) {
  return scoreAt(index) == Mark;
}

bool BowlingScoreCard::isSpareAt(size_type index) {
  return !isStrikeAt(index) && nextTwoRollsSummed(index) == Mark;
}

int BowlingScoreCard::scoreStrikeAt(size_type index) {
  return Mark + nextTwoRollsSummed(index + 1);
}

int BowlingScoreCard::scoreSpareAt(size_type index) {
  return Mark + scoreAt(index + 2);
}

int BowlingScoreCard::frameSizeAt(size_type index) {
  if(scoreAt(index) == Mark)
    return 1;
  return 2;
}

int BowlingScoreCard::nextTwoRollsSummed(size_type index) {
  return scoreAt(index) + scoreAt(index + 1);
}

int BowlingScoreCard::scoreAt(size_type index) {
  return index < rolls.size() ? rolls[index] : 0;
}

void BowlingScoreCard::recordThrow(int roll) {
  rolls.push_back(roll);
}

I sure do miss refactoring tools!-)

Hiding global methods, a C++ example 32

Posted by Brett Schuchert Fri, 12 Jun 2009 15:13:00 GMT

Background

This is a continuation of this discussion. A few of the postings by GBGames have been swallowed so the context is somewhat lost. What follows is an excerpt from an email he sent me and some example C++ code that demonstrates making global methods “testable” in a sense. Or rather, making your code not use global methods during test.

The Offending Method

Here is a method that GBGames has under test:
void SDLHardwareLayer::initializeHardware()
{
    const SDL_version * sdlVersion = SDL_Linked_Version();
    if (sdlVersion->minor < 2 && sdlVersion->major < 2)
    {
        //Error message
    }
    else if (SDL_Init(SDL_INIT_VIDEO |
            SDL_INIT_AUDIO |
            SDL_INIT_NOPARACHUTE) < 0)
    {
        //Error message
    }
    else
    {
        m_screen = SDL_SetVideoMode(m_x, m_y, m_bitDepth, SDL_DOUBLEBUF);
        if (NULL != m_screen)
        {
            SDL_WM_SetCaption(m_title.c_str(), 0);
        }
    }
}

This is how this code is organized:

So What’s Wrong?

One problem he notes is that the test code driving this production code causes windows to pop up during testing. Is this a problem? If it works, then it may be OK. However, you can remove that and also make test-ability a bit easier. You’ll be testing interactions and responses rather than directly using the library through its global functions.

Global Functions Considered Harmful

First off, this code as written directly uses global functions. If you leave this code unchanged, then the only option you have to “fix” the problem of windows popping up is a link-seam (see Working Effective with Legacy Code, page 233-234. In a nutshell, a link seam will link different versions of a library, one for testing, one for actual execution. So you’ll need some “make fu” to get the link seam working in a manner that easily allows building for testing versus building for execution. Since we’re using C++ and we own this code, I chose a different route:
  • Change this code to use an object that uses the library
  • That object implements an interface (a base class with all pure virtual methods)
  • Change this code so that it acquires this object through a factory (I could have simply used constructor injection, but I’ve already written it using a configurable factory [really singleton] – but it would have been simpler if I had injected the interface)
  • Created a test double that can be used when writing new test doubles making code maintenance a bit easier (mentioned in the other blog posting).
  • Write test-method specific test doubles

Changing Code to Use Factory + Object w/virtual Methods

Rather than directly calling the various global SDL_* methods, e.g.:
const SDL_version * sdlVersion = SDL_Linked_Version();
I instead use a factory to get an instance and call
const SDL_version * sdlVersion = SdlLayerFactory::getInstance()->SDL_Linked_Version();

Base Interface

To make this work, I created a base interface representing all of the methods needed by the SDLHardwareLayer::initializeHardware:
#pragma once

struct SDL_version;

class SdlLayerInstanceDelegator {
public:
  virtual ~SdlLayerInstanceDelegator(void) = 0;

  virtual const SDL_version *SDL_Linked_Version() = 0;
  virtual int SDL_Init(int bitMask) = 0;
  virtual void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode) = 0;
  virtual void SDL_WM_SetCaption(const char *title, int someIntValue) = 0;

protected:
  SdlLayerInstanceDelegator();

private:
  SdlLayerInstanceDelegator(const SdlLayerInstanceDelegator &rhs);
  SdlLayerInstanceDelegator &operator = (const SdlLayerInstanceDelegator &);
};

Real Calls Global Methods

Getting rid of the direct call to the global method makes this work. However, you still need to call those global methods somewhere. That’s where the “real” version of the interface comes in. The real version of this class simply calls the global methods (code only, not showing header file right now):
const SDL_version *RealSdlLayerInstanceDelegator::SDL_Linked_Version() {
  return ::SDL_Linked_Version();
}

int RealSdlLayerInstanceDelegator::SDL_Init(int bitMask) {
  return ::SDL_Init(bitMask);
}

void *RealSdlLayerInstanceDelegator::SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return ::SDL_SetVideoMode(x, y, bitDepth, mode);
}

void RealSdlLayerInstanceDelegator::SDL_WM_SetCaption(const char *title, int someIntValue) {
  ::SDL_WM_SetCaption(title, someIntValue);
}

The Factory: Inversion of Control

The original code directly used global methods. We need to invert this relationship so that rather than depending directly on those methods, it depends on an interface. Then if it happens that it uses a “real” version, the underlying code will call the actual library. If, however, it is instead using a test double, it will not. It will do whatever the test double dictates.

We cannot leave the decision of which object to talk to in the SdlHardwareLayer class. Instead, I’ve used a configurable factory (singleton). A test can configure the factory, call the method under test, and then reset the factory back after the test.

The SdlLayerFactory allows such use(again, code only, no header file):
SdlLayerInstanceDelegator *SdlLayerFactory::instance = 0;

SdlLayerInstanceDelegator *SdlLayerFactory::getInstance() {
  return instance;
}

SdlLayerInstanceDelegator *SdlLayerFactory::replaceInstance(SdlLayerInstanceDelegator *replacement) {
  SdlLayerInstanceDelegator *original = instance;
  instance = replacement;
  return original;
}

Handling Growing Interfaces

One problem with “interfaces” in any language is that as you add methods to them, their implementations need to be updated. Since the SdlLayerInstanceDelegator class is not complete yet, this will case problems with test doubles. There are three ways to handle this problem: * Deal with it, as you add new methods to the interface, update all existing classes * Use a mocking library – I have not used any for C++, but if this were Java I’d use Mockito and if this were .Net I’d use Moq. * Create a base test double that implements all of the methods. All test doubles derive from that and only implement the methods needed for test.

The last option does not fix the problem, it controls it. When new methods are added, you update one test double class and all other classes still work.

This may not sound like much of an issue, but in general you’ll have many small test doubles rather than a few large test doubles (or at least that’s the standard recommendation). Why? Because a small, focused test double is less likely to be broken. It also better expressed your intention.

Here is that test double (header file excluded again): TestDoubleSdlLayerInstanceDelegator.h

#pragma once
#include "SdlLayerInstanceDelegator.h"

class TestDoubleSdlLayerInstanceDelegator: public SdlLayerInstanceDelegator {
public:
  TestDoubleSdlLayerInstanceDelegator();
  virtual ~TestDoubleSdlLayerInstanceDelegator();

  const SDL_version *SDL_Linked_Version();
  int SDL_Init(int bitMask);
  void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode);
  void SDL_WM_SetCaption(const char *title, int someIntValue);
};

TestDoubleSdlLayerInstanceDelegator.cpp

#include "TestDoubleSdlLayerInstanceDelegator.h"

TestDoubleSdlLayerInstanceDelegator::TestDoubleSdlLayerInstanceDelegator(){}

TestDoubleSdlLayerInstanceDelegator::~TestDoubleSdlLayerInstanceDelegator(){}

const SDL_version *TestDoubleSdlLayerInstanceDelegator::SDL_Linked_Version() {
  return 0;
}

int TestDoubleSdlLayerInstanceDelegator::SDL_Init(int bitMask) {
  return 0;
}

void *TestDoubleSdlLayerInstanceDelegator::SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return 0;
}

void TestDoubleSdlLayerInstanceDelegator::SDL_WM_SetCaption(const char *title, int someIntValue){}

A Test

Finally, we need some tests and some test-specific test-doubles:
#include <CppUTest/TestHarness.h>

#include "sdl.h"
#include "SdlLayerFactory.h"
#include "SdlHardwareLayer.h"
#include "TestDoubleSdlLayerInstanceDelegator.h"

TEST_GROUP(SdlHardwareLayerTest) {
  virtual void setup() {
    original = SdlLayerFactory::getInstance();
    layer = new SdlHardwareLayer;
  }

  virtual void teardown() {
    if(SdlLayerFactory::getInstance() != original)
      delete SdlLayerFactory::getInstance();

    SdlLayerFactory::replaceInstance(original);
    delete layer;
  }

  SdlLayerInstanceDelegator *original;
  SdlHardwareLayer *layer;
};

struct MajorMinorSdlhardwareLayer : public TestDoubleSdlLayerInstanceDelegator {
  struct SDL_version v;

  MajorMinorSdlhardwareLayer() {
    v.major = 0;
    v.minor = 0;
  }

  const SDL_version *SDL_Linked_Version() {
    return &v;
  }
};

TEST(SdlHardwareLayerTest, ItGeneratsErrorWithLowMajorMinorVersionNumber) {
  SdlLayerFactory::replaceInstance(new MajorMinorSdlhardwareLayer);
  try {
    layer->initializeHardware();
    FAIL("Should have thrown int value");
  } catch (int value) {
    LONGS_EQUAL(1, value);
  }
}

struct InitFailingSdlHardwareLayer : public MajorMinorSdlhardwareLayer {
  InitFailingSdlHardwareLayer () {
    v.major = 3;
    v.minor = 3;
  }

  int SDL_Init(int bitMask) {
    return -1;
  }
};

TEST(SdlHardwareLayerTest, ItGeneratesErrorWhenInitReturnsLessThan0) {
  SdlLayerFactory::replaceInstance(new InitFailingSdlHardwareLayer);
  try {
    layer->initializeHardware();
    FAIL("Should have thrown int value");
  } catch (int value) {
    LONGS_EQUAL(2, value);
  }
}

Notice that there are two test doubles. Also notice that I create them as full (implicitly) inlined classes. Virtual methods and inline methods do not interact well. C++ will make a copy of the method bodies in every .o and increase link times. However, these test classes are only used in one place, so this really does not cause any problems.

In lieu of a mocking library, this is how I’d really write this code.

The Final Big Picture

Here’s an image of the completed product:

Summary

So is this overkill? Is using the actual SDL library causing problems? Is it OK for windows to appear during test? I’m neutral on that subject. The question I want to know is: are the tests working?

  • Do they add value or are they busy work.
  • Do they allow me to make small, incremental steps towards a complete, working implementation
  • Do they run fast enough?
  • Will they run in any environment? (Can I run on a headless system?)
  • Can I run multiple tests at the same time? (Not generally an issue, but it can be.)

If the SDL library is not causing a problem, then this might be overkill. However, on a long-lived project, this little amount of extra work will pay big dividends.

Oh, how long did this really take? Around 1.5 hours. But that included:
  • Starting a Windows XP VM
  • Creating a new projec
  • Setting up the link paths and include paths
  • Creating the initial file and the necessary support to get it to compile
  • Writing the additional classes

In fact, the actual work was probably < 30 minutes total. So while this might look big, in fact it is not. It’s nearly an idiom, so it’s mostly a matter of putting the hooks in place. So is full isolation from SDL worth 30 minutes?

Yes!

All your source files are belong to us

If this looks like a complete example, it is. I have this building and running in Visual Studio 2008. I took GBGame’s original method and got it to compile and link with a minimal set of additional source files. Then I made the structural changes to support test. And here’s all of the source code, file by file:

sdl.h

#pragma once

extern "C" {
  const int SDL_INIT_VIDEO = 1;
  const int SDL_INIT_AUDIO = 2;
  const int SDL_INIT_NOPARACHUTE = 4;
  const int SDL_DOUBLEBUF = 8;

  struct SDL_version {
    int major;
    int minor;
  };

  const SDL_version *SDL_Linked_Version();
  int SDL_Init(int bitMask);
  void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode);
  void SDL_WM_SetCaption(const char *title, int someIntValue);
};

sdl_implementation.cpp

#include "sdl.h"

const SDL_version *SDL_Linked_Version() {
  return 0;
}

int SDL_Init(int bitMask) {
  return  - 1;
}

void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return 0;
}

void SDL_WM_SetCaption(const char *title, int someIntValue){}

SdlHardwareLayer.h
#pragma once

#include <string>

class SdlHardwareLayer {
public:
  SdlHardwareLayer();
  virtual ~SdlHardwareLayer();
  void initializeHardware();

private:
  int m_x;
  int m_y;
  int m_bitDepth;
  std::string m_title;
  void *m_screen;
};

SdlHardwarelayer.cpp

#include "SdlHardwareLayer.h"

#include "sdl.h"
#include "SdlLayerFactory.h"
#include "SdlLayerInstanceDelegator.h"

SdlHardwareLayer::SdlHardwareLayer() {
}

SdlHardwareLayer::~SdlHardwareLayer() {
}

void SdlHardwareLayer::initializeHardware() {
    const SDL_version * sdlVersion = SdlLayerFactory::getInstance()->SDL_Linked_Version();
    if (sdlVersion->minor < 2 && sdlVersion->major < 2) {
      throw 1;
    }
    else if (SdlLayerFactory::getInstance()->SDL_Init(SDL_INIT_VIDEO |
            SDL_INIT_AUDIO |
            SDL_INIT_NOPARACHUTE) < 0) {
              throw 2;
    }
    else {
        m_screen = SDL_SetVideoMode(m_x, m_y, m_bitDepth, SDL_DOUBLEBUF);
        if (0 != m_screen) {
            SDL_WM_SetCaption(m_title.c_str(), 0);
        }
    }
}

SdlLayerFactory.h
#pragma once

class SdlLayerInstanceDelegator;

class SdlLayerFactory
{
public:
  static SdlLayerInstanceDelegator *getInstance();
  static SdlLayerInstanceDelegator *replaceInstance(SdlLayerInstanceDelegator *replacement);

private:
  static SdlLayerInstanceDelegator *instance;

  SdlLayerFactory();
  ~SdlLayerFactory();
};

SdlLayerFactory.cpp

#include "SdlLayerFactory.h"

SdlLayerInstanceDelegator *SdlLayerFactory::instance = 0;

SdlLayerFactory::SdlLayerFactory(){}

SdlLayerFactory::~SdlLayerFactory(){}

SdlLayerInstanceDelegator *SdlLayerFactory::getInstance() {
  return instance;
}

SdlLayerInstanceDelegator *SdlLayerFactory::replaceInstance(SdlLayerInstanceDelegator *replacement) {
  SdlLayerInstanceDelegator *original = instance;
  instance = replacement;
  return original;
}

SdlLayerInstanceDelegator.h
#pragma once

struct SDL_version;

class SdlLayerInstanceDelegator {
public:
  virtual ~SdlLayerInstanceDelegator(void) = 0;

  virtual const SDL_version *SDL_Linked_Version() = 0;
  virtual int SDL_Init(int bitMask) = 0;
  virtual void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode) = 0;
  virtual void SDL_WM_SetCaption(const char *title, int someIntValue) = 0;

protected:
  SdlLayerInstanceDelegator();

private:
  SdlLayerInstanceDelegator(const SdlLayerInstanceDelegator &rhs);
  SdlLayerInstanceDelegator &operator = (const SdlLayerInstanceDelegator &);
};

SdlLayerInstanceDelegator.cpp

#include "SdlLayerInstanceDelegator.h"

SdlLayerInstanceDelegator::SdlLayerInstanceDelegator(){}

SdlLayerInstanceDelegator::~SdlLayerInstanceDelegator(){}

TestDoubleSdlLayerInstanceDelegator.h
#pragma once
#include "SdlLayerInstanceDelegator.h"

class TestDoubleSdlLayerInstanceDelegator: public SdlLayerInstanceDelegator {
public:
  TestDoubleSdlLayerInstanceDelegator();
  virtual ~TestDoubleSdlLayerInstanceDelegator();

  const SDL_version *SDL_Linked_Version();
  int SDL_Init(int bitMask);
  void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode);
  void SDL_WM_SetCaption(const char *title, int someIntValue);
};

TestDoubleSdlLayerInstanceDelegator.cpp

#include "TestDoubleSdlLayerInstanceDelegator.h"

TestDoubleSdlLayerInstanceDelegator::TestDoubleSdlLayerInstanceDelegator(){}

TestDoubleSdlLayerInstanceDelegator::~TestDoubleSdlLayerInstanceDelegator(){}

const SDL_version *TestDoubleSdlLayerInstanceDelegator::SDL_Linked_Version() {
  return 0;
}

int TestDoubleSdlLayerInstanceDelegator::SDL_Init(int bitMask) {
  return 0;
}

void *TestDoubleSdlLayerInstanceDelegator::SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return 0;
}

void TestDoubleSdlLayerInstanceDelegator::SDL_WM_SetCaption(const char *title, int someIntValue){}

RealSdlLayerInstanceDelegator.h
#pragma once
#include "SdlLayerInstanceDelegator.h"

class RealSdlLayerInstanceDelegator : public SdlLayerInstanceDelegator
{
public:
  RealSdlLayerInstanceDelegator();
  virtual ~RealSdlLayerInstanceDelegator();

  const SDL_version *SDL_Linked_Version();
  int SDL_Init(int bitMask);
  void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode);
  void SDL_WM_SetCaption(const char *title, int someIntValue);
};

RealSdlLayerInstanceDelegator.cpp

#include "RealSdlLayerInstanceDelegator.h"
#include "sdl.h"

RealSdlLayerInstanceDelegator::RealSdlLayerInstanceDelegator(){}

RealSdlLayerInstanceDelegator::~RealSdlLayerInstanceDelegator(){}

const SDL_version *RealSdlLayerInstanceDelegator::SDL_Linked_Version() {
  return ::SDL_Linked_Version();
}

int RealSdlLayerInstanceDelegator::SDL_Init(int bitMask) {
  return ::SDL_Init(bitMask);
}

void *RealSdlLayerInstanceDelegator::SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return ::SDL_SetVideoMode(x, y, bitDepth, mode);
}

void RealSdlLayerInstanceDelegator::SDL_WM_SetCaption(const char *title, int someIntValue) {
  ::SDL_WM_SetCaption(title, someIntValue);
}

SdlHardwareLayerTest.h
#include <CppUTest/TestHarness.h>

#include "sdl.h"
#include "SdlLayerFactory.h"
#include "SdlHardwareLayer.h"
#include "TestDoubleSdlLayerInstanceDelegator.h"

TEST_GROUP(SdlHardwareLayerTest) {
  virtual void setup() {
    original = SdlLayerFactory::getInstance();
    layer = new SdlHardwareLayer;
  }

  virtual void teardown() {
    if(SdlLayerFactory::getInstance() != original)
      delete SdlLayerFactory::getInstance();

    SdlLayerFactory::replaceInstance(original);
    delete layer;
  }

  SdlLayerInstanceDelegator *original;
  SdlHardwareLayer *layer;
};

struct MajorMinorSdlhardwareLayer : public TestDoubleSdlLayerInstanceDelegator {
  struct SDL_version v;

  MajorMinorSdlhardwareLayer() {
    v.major = 0;
    v.minor = 0;
  }

  const SDL_version *SDL_Linked_Version() {
    return &v;
  }
};

TEST(SdlHardwareLayerTest, ItGeneratsErrorWithLowMajorMinorVersionNumber) {
  SdlLayerFactory::replaceInstance(new MajorMinorSdlhardwareLayer);
  try {
    layer->initializeHardware();
    FAIL("Should have thrown int value");
  } catch (int value) {
    LONGS_EQUAL(1, value);
  }
}

struct InitFailingSdlHardwareLayer : public MajorMinorSdlhardwareLayer {
  InitFailingSdlHardwareLayer () {
    v.major = 3;
    v.minor = 3;
  }

  int SDL_Init(int bitMask) {
    return -1;
  }
};

TEST(SdlHardwareLayerTest, ItGeneratesErrorWhenInitReturnsLessThan0) {
  SdlLayerFactory::replaceInstance(new InitFailingSdlHardwareLayer);
  try {
    layer->initializeHardware();
    FAIL("Should have thrown int value");
  } catch (int value) {
    LONGS_EQUAL(2, value);
  }
}

RunAllTests.cpp
#include <CppUTest/CommandLineTestRunner.h>

int main(int ac, char **av) {
  return CommandLineTestRunner::RunAllTests(ac, av);
}

Slim table examples with fixtures in C# 81

Posted by Brett Schuchert Tue, 09 Jun 2009 04:15:00 GMT

There was not a lot of interest in a series of tutorials. Some, yes, but not very much. So I’m not going to be spending much time right now writing FitNesse.Slim tutorials for C#.

However, I think it’s a good idea to at least have some examples of each of the tables so those few that are so inclined, have something from which to start.

Have a look: C# Slim Examples

Calling C# and FitNesse Users 52

Posted by Brett Schuchert Sat, 06 Jun 2009 05:43:00 GMT

Are you using C# and FitNesse? Considering moving to FitNesse.Slim?

I eventually plan to port several FitNesse.Slim tutorials written in Java to C#. It’s a bit of work so I want to make sure there’s enough interest now (this is about when not if). Given that C# and Java are similar languages, I’m not convinced it’s really necessary right now. I’m under the impression that the user base for FitNesse.Slim + C# is small, but I’m often wrong. I have other things I want to get around to writing, so I’m trying to get a temperature reading to know what might be useful next.

If this would help you or your team, what would you like to see?
  • Just the basics, all the tables and an example of each (quick to do)
  • Something more comprehensive, building story tests and working through to TDD to build a system from the ground up?
  • A full porting of the existing tutorials as is?
  • ...

I’m really looking for some user stories from some actual candidate users. If that’s you or you know someone and you like being a proxy, please respond.

Thanks!

Brett

Moq examples, Part II 62

Posted by Brett Schuchert Tue, 26 May 2009 02:57:00 GMT

A few blog articles ago I showed some examples using Moq. I created those examples in anticipation of using Moq in a TDD class with a group in Canada.

What I ended up creating was different for a number of reasons. The final version is somewhat cleaned up compared to those first examples, so here are those examples as well: actual test class created.

These tests are not really that different from the first version, other than I did not use exceptions as much.

After the underlying LoginService was created with these tests, I had the students refactor to the GoF State Design Pattern. If you’re feeling like a little practice, consider:
  • Creating one test at a time
  • Write the underlying service
  • When all tests are passing, try to refactor to the state pattern.

It’s a good exercise. The guys in Canada seemed to really grok Moq and started using it to characterize their actual code the next day.

Older posts: 1 2 3