unit test for GUI code

Discussion in 'C++' started by shuisheng, Feb 22, 2007.

  1. shuisheng

    shuisheng Guest

    Dear All,

    I was told that unit test is a powerful tool for progamming. If I am
    writing a GUI code, is it possible to still using unit test?

    I have a little experience in using unittest++. But I can not work out
    a way to use it to test GUI code.

    Thanks a lot!

    Shuisheng
    shuisheng, Feb 22, 2007
    #1
    1. Advertising

  2. On Feb 22, 11:06 pm, "shuisheng" <> wrote:
    > Dear All,
    >
    > I was told that unit test is a powerful tool for progamming. If I am
    > writing a GUI code, is it possible to still using unit test?
    >
    > I have a little experience in using unittest++. But I can not work out
    > a way to use it to test GUI code.
    >
    > Thanks a lot!
    >
    > Shuisheng


    Do a google for 'TFUI' - its a good technique for your problem.

    Aside from that, the main approach is to treat the GUI library like
    any other 3rd party library - mainly, don't try and unit test it, unit
    test your code that uses it.

    By this, I mean: ensure all of your logic (business rules, gui
    presentation rules, data retrival and updates, etc) are completed
    separated from the gui code. In your gui's event handlers, don't do
    anything except delegate to a plain old C++ Class.

    By separating your code from the gui code, you can easily test your
    code, without having to even link to the gui library.

    In software, abstract is usually the way to solve a problem.

    Andrew
    andrewmcdonagh, Feb 22, 2007
    #2
    1. Advertising

  3. shuisheng

    Michael Guest

    > I was told that unit test is a powerful tool for progamming. If I am
    > writing a GUI code, is it possible to still using unit test?


    I have an approach based on some stuff I read in Michael Feather's
    book. It is not as developed as what Andrew posted (for example, I
    don't have a clever name for it), but it works for me.

    I've been doing MFC programming, so I'll give my example there,
    although it's just an example. In MFC, there's a class called CDC,
    which is the C (language) device context. It has a bunch of primitive
    drawing methods like:

    CPoint MoveTo(int x, int y);

    Now I'm writing a class C that does a bunch of drawing, so it winds up
    calling these. It has a method OnDraw that takes a CDC* and does the
    drawing (in turn calling a bunch of other methods). This is the
    method I want to test.

    So basically, I created a class called DCInterface, which looks like
    this:
    class DCInterface {
    public:
    virtual CPoint MoveTo(int x, int y) = 0;
    /* etc. */
    };

    Then, I created a class CDCWrapper that calls the actual one:

    class CDCWrapper : public DCInterface {
    private:
    CDC* pDC;
    public:
    explicit CDCWrapper(CDC* p) : pDC(p) {}
    CPoint MoveTo(int x, int y) { return pDC->MoveTo(x, y); }
    /* etc. */

    Then I refactor my code for class C to use a DCInterface instead of a
    CDC. (The OnDraw method stays, since the MFC framework calls it, but
    now it just creates a CDCWrapper and forwards on to the wrapped
    version of OnDraw.)

    So far, everything is just setup for unit testing.

    Now, here's the cool part. I created a TestDC class that implements
    all of the functionality by storing a string with messages like
    "MoveTo(5, 6) called.\n";

    So to do unit tests, I set everything up with my TestDC class, and the
    output is a string (instead of drawing something on the screen). I
    can then do all my assertions that the appropriate series of draw
    operations were called, based on looking at the output string.

    This obviously doesn't do everything, but it lets me unit test some
    fairly complex graphics routines.

    More generally, you can pull out a wrapper that has virtual copies of
    the library interface. Create an "real" implementation class that
    just delegates to the library, then create a separate "test"
    implementation class that stores things to a string (or some other
    structure that's easy to use in unit tests).

    There's a fair amount of work to create the wrappers in the first
    place - I took the approach of "only wrap functions you actually use,"
    but that's a one-time cost and then you can reuse it for your unit
    testing.

    Michael
    Michael, Feb 23, 2007
    #3
  4. On Feb 23, 2:11 am, "Michael" <> wrote:
    > > I was told that unit test is a powerful tool for progamming. If I am
    > > writing a GUI code, is it possible to still using unit test?

    >
    > I have an approach based on some stuff I read in Michael Feather's
    > book. It is not as developed as what Andrew posted (for example, I
    > don't have a clever name for it), but it works for me.
    >
    > I've been doing MFC programming, so I'll give my example there,
    > although it's just an example. In MFC, there's a class called CDC,
    > which is the C (language) device context. It has a bunch of primitive
    > drawing methods like:
    >
    > CPoint MoveTo(int x, int y);
    >
    > Now I'm writing a class C that does a bunch of drawing, so it winds up
    > calling these. It has a method OnDraw that takes a CDC* and does the
    > drawing (in turn calling a bunch of other methods). This is the
    > method I want to test.
    >
    > So basically, I created a class called DCInterface, which looks like
    > this:
    > class DCInterface {
    > public:
    > virtual CPoint MoveTo(int x, int y) = 0;
    > /* etc. */
    >
    > };
    >
    > Then, I created a class CDCWrapper that calls the actual one:
    >
    > class CDCWrapper : public DCInterface {
    > private:
    > CDC* pDC;
    > public:
    > explicit CDCWrapper(CDC* p) : pDC(p) {}
    > CPoint MoveTo(int x, int y) { return pDC->MoveTo(x, y); }
    > /* etc. */
    >
    > Then I refactor my code for class C to use a DCInterface instead of a
    > CDC. (The OnDraw method stays, since the MFC framework calls it, but
    > now it just creates a CDCWrapper and forwards on to the wrapped
    > version of OnDraw.)
    >
    > So far, everything is just setup for unit testing.
    >
    > Now, here's the cool part. I created a TestDC class that implements
    > all of the functionality by storing a string with messages like
    > "MoveTo(5, 6) called.\n";
    >
    > So to do unit tests, I set everything up with my TestDC class, and the
    > output is a string (instead of drawing something on the screen). I
    > can then do all my assertions that the appropriate series of draw
    > operations were called, based on looking at the output string.
    >
    > This obviously doesn't do everything, but it lets me unit test some
    > fairly complex graphics routines.
    >
    > More generally, you can pull out a wrapper that has virtual copies of
    > the library interface. Create an "real" implementation class that
    > just delegates to the library, then create a separate "test"
    > implementation class that stores things to a string (or some other
    > structure that's easy to use in unit tests).
    >
    > There's a fair amount of work to create the wrappers in the first
    > place - I took the approach of "only wrap functions you actually use,"
    > but that's a one-time cost and then you can reuse it for your unit
    > testing.
    >
    > Michael


    Its a great book for working with un tested code, and for seeing how
    to create 'seams' between areas like GUI libraries and our code.

    For those interested in unit testing, a good guide that M Features,
    myself and plenty of others use is:

    A unit test does NOT :
    * Use any file IO
    * Connect to a db
    * Present anything on screen
    * communicate across a network.

    The rational for this, is many, but boils down to forcing us to create
    a design that is decoupled from these areas, which nearly always
    results in a good, clean & highly cohesive design. For example, how
    many times have we in the past put logic directly into a dialog
    class? Where as with this style of testing, we force ourselves to
    separate out the UI aspect from the logic part, resulting in us using
    the MVC or MVP or Humble Dialog design patterns, as they allow us to
    unit test th elogic without going anywhere near the GUI.

    The second major win is speed of test runs.

    I can run 1200 unit tests in 42.7 seconds currently (and these are
    Java unit tests!) because of following these guidelines. And because
    they run soo fast the Team runs ALL tests ALL of the time.
    Slow tests mean we run them fewer times, the less we run them, the
    longer it is before we find out we broken something. The longer time
    before finding out we broke something, the more costly it is to
    fix.....

    Andrew
    andrewmcdonagh, Feb 23, 2007
    #4
  5. shuisheng

    shuisheng Guest

    On Feb 22, 9:11 pm, "Michael" <> wrote:
    > > I was told that unit test is a powerful tool for progamming. If I am
    > > writing a GUI code, is it possible to still using unit test?

    >
    > I have an approach based on some stuff I read in Michael Feather's
    > book. It is not as developed as what Andrew posted (for example, I
    > don't have a clever name for it), but it works for me.
    >
    > I've been doing MFC programming, so I'll give my example there,
    > although it's just an example. In MFC, there's a class called CDC,
    > which is the C (language) device context. It has a bunch of primitive
    > drawing methods like:
    >
    > CPoint MoveTo(int x, int y);
    >
    > Now I'm writing a class C that does a bunch of drawing, so it winds up
    > calling these. It has a method OnDraw that takes a CDC* and does the
    > drawing (in turn calling a bunch of other methods). This is the
    > method I want to test.
    >
    > So basically, I created a class called DCInterface, which looks like
    > this:
    > class DCInterface {
    > public:
    > virtual CPoint MoveTo(int x, int y) = 0;
    > /* etc. */
    >
    > };
    >
    > Then, I created a class CDCWrapper that calls the actual one:
    >
    > class CDCWrapper : public DCInterface {
    > private:
    > CDC* pDC;
    > public:
    > explicit CDCWrapper(CDC* p) : pDC(p) {}
    > CPoint MoveTo(int x, int y) { return pDC->MoveTo(x, y); }
    > /* etc. */
    >
    > Then I refactor my code for class C to use a DCInterface instead of a
    > CDC. (The OnDraw method stays, since the MFC framework calls it, but
    > now it just creates a CDCWrapper and forwards on to the wrapped
    > version of OnDraw.)
    >
    > So far, everything is just setup for unit testing.
    >
    > Now, here's the cool part. I created a TestDC class that implements
    > all of the functionality by storing a string with messages like
    > "MoveTo(5, 6) called.\n";
    >
    > So to do unit tests, I set everything up with my TestDC class, and the
    > output is a string (instead of drawing something on the screen). I
    > can then do all my assertions that the appropriate series of draw
    > operations were called, based on looking at the output string.
    >
    > This obviously doesn't do everything, but it lets me unit test some
    > fairly complex graphics routines.
    >
    > More generally, you can pull out a wrapper that has virtual copies of
    > the library interface. Create an "real" implementation class that
    > just delegates to the library, then create a separate "test"
    > implementation class that stores things to a string (or some other
    > structure that's easy to use in unit tests).
    >
    > There's a fair amount of work to create the wrappers in the first
    > place - I took the approach of "only wrap functions you actually use,"
    > but that's a one-time cost and then you can reuse it for your unit
    > testing.
    >
    > Michael


    Hi, Michael,

    Thanks a lot for your reply. What I understand is that I write a class
    to simulate the behavior of the GUI class. The new class uses some
    string
    information to substitute the graphical and interactive operations. Is
    my
    understanding right?

    Thanks,

    Shuisheng
    include the Graphic
    shuisheng, Feb 23, 2007
    #5
  6. shuisheng

    Michael Guest

    On Feb 23, 8:36 am, "shuisheng" <> wrote:
    > Thanks a lot for your reply. What I understand is that I write a class
    > to simulate the behavior of the GUI class. The new class uses some
    > string
    > information to substitute the graphical and interactive operations. Is
    > my
    > understanding right?


    Yes, you basically write a class that you use only for testing that
    makes it easier to test things.

    And Andrew is right - if it's hard to do that, it indicates that maybe
    you should move some of your logic into a different class (and have
    your original class call it), where your new class is specifically
    designed for testability.

    And finally, the Feather book is truly wonderful. One of the best
    I've read in the last year, maybe several years.

    Michael
    Michael, Feb 23, 2007
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. VvanN
    Replies:
    5
    Views:
    485
    Phlip
    Apr 28, 2006
  2. Bill David
    Replies:
    2
    Views:
    270
    Arne Vajhøj
    Jun 18, 2008
  3. Bill Mosteller
    Replies:
    0
    Views:
    216
    Bill Mosteller
    Oct 22, 2009
  4. James Wenton
    Replies:
    3
    Views:
    247
    Ryan Davis
    May 26, 2010
  5. timr
    Replies:
    2
    Views:
    161
Loading...

Share This Page