Project

General

Profile

unit testing widgets

Added by Paolo Greppi over 11 years ago

Separating the application logic from the user interface allows to test the application-specific classes without linking in Wt. But ultimately the UI classes will have some sort of residual intelligence, an internal state, and correspondingly utility methods such as "resize" or "sort". What is the best practice for testing these for example with boost::test ?

The problem is, for classes derived from Wt::WWidget I need to pass Wt::WContainerWidget *parent; if that is NULL, trouble occurs.

To get a handle to a Wt::WContainerWidget, I need a Wt::WApplication. To get a Wt::WApplication, I need a Wt::WEnvironment. But I can't build a Wt::WEnvironment as the constructor is protected.

This is the (non compiling) code I have at the moment:

Wt::WApplication *app;

BOOST_AUTO_TEST_CASE(init) {
  Wt::WEnvironment env; // <==== FAILS
  app = new Wt::WApplication(env);
} // init

BOOST_AUTO_TEST_CASE(resize) {
  //         name        happiness  containing widget
  MyWidget a("Carnival", true,      app->root());
  BOOST_CHECK(a.name() == "Carnival");
  BOOST_CHECK(a.isHappy());
  BOOST_CHECK(a.size() == 0);
  a.resize(10);
  BOOST_CHECK(a.size() == 10);
} // resize

BOOST_AUTO_TEST_CASE(cleanup) {
  delete app;
} // cleanup

Any ideas ?


Replies (4)

RE: unit testing widgets - Added by Paolo Greppi over 11 years ago

Thanks I got past the environment initialization problem. I should have looked at the similar answer Hilary Cheng got in witty-interest on 08 Jun 2010.

Unforrtunately that does not solve all the problems.

To exemplify, I have tried a toy test, testing the behavior of one basic widget (WLabel). This is the code:

// compile:
//   g++ -o TestLabel TestLabel.cc -I ../wt-3.1.10/src -I ../wt-3.1.10/build/ -L ../wt-3.1.10/build/src -lwt
// prepare:
//   ln -s ../wt-3.1.10/build/src/libwt.so.29 .

#define BOOST_TEST_MODULE Label

#include <boost/test/included/unit_test.hpp>

#include <Wt/WApplication>
#include <Wt/Test/WTestEnvironment>
#include <Wt/WLabel>

Wt::WApplication *app;

BOOST_AUTO_TEST_CASE(init) {
  app = new Wt::WApplication(Wt::Test::WTestEnvironment());
  BOOST_REQUIRE(app != NULL);
} // init

BOOST_AUTO_TEST_CASE(WLabel) {
  Wt::WApplication::UpdateLock lock(app);
  BOOST_REQUIRE(lock);
  Wt::WLabel *l = new Wt::WLabel("aaa", app->root());
  BOOST_CHECK(l->text().toUTF8() == "aaa");
  l->setText("qqq");
  BOOST_CHECK(l->text().toUTF8() == "qqq");
} // WLabel

BOOST_AUTO_TEST_CASE(cleanup) {
  delete app;
} // cleanup

This is what happens when I run it:

$ /lib/ld-linux.so.2 --library-path . ./TestLabel --build_info=yes --log_level=test_suite
Running 3 test cases...
Platform: linux
Compiler: GNU C++ version 4.7.2
STL     : GNU libstdc++ version 20120920
Boost   : 1.49.0
Entering test suite "Label"
Entering test case "init"
Reading: /etc/wt/wt_config.xml
[2013-Jan-24 10:25:16.106787] 15386 [/ testwtd] [notice] "Session created (#sessions = 1)"
[2013-Jan-24 10:25:16.107533] 15386 [/ testwtd] [notice] "Session destroyed (#sessions = 0)"
Leaving test case "init"
Entering test case "WLabel"
TestLabel.cc(25): fatal error in "WLabel": critical check lock failed
Leaving test case "WLabel"
Entering test case "cleanup"
unknown location(0): fatal error in "cleanup": memory access violation at address: 0x00000004: no mapping at fault address
Test is aborted
Leaving test case "cleanup"
Leaving test suite "Label"

*** 2 failures detected in test suite "Label"

To me it seems like the session is created, and immediately destroyed, even before the test case "init" is finished. This is confirmed by the fatal error when trying to deallocate the WApplication in "cleanup". And it would explain why it does not get the application lock.

Thanks for your help

RE: unit testing widgets - Added by Wim Dumon over 11 years ago

Hello Paolo,

Your WEnvironment object is too short-lived. It must last longer than WApplciation.

Also I don't think boost guarantees anything about order of execution of test-cases (I've seen them being different on windows/unix), so expect problems with your approach. Boost also allows each test to be executed separately, which won't work for you neither.

BOOST_AUTO_TEST_CASE(WLabel) {
  Wt::Test::WTestEnvironment env;
  Wt::Wapplication *app = new Wt::WApplication(env);
  BOOST_REQUIRE(app != NULL);
  Wt::WApplication::UpdateLock lock(app);
  BOOST_REQUIRE(lock);
  Wt::WLabel *l = new Wt::WLabel("aaa", app->root());
  BOOST_CHECK(l->text().toUTF8() == "aaa");
  l->setText("qqq");
  BOOST_CHECK(l->text().toUTF8() == "qqq");
  delete app;
} // WLabel

RE: unit testing widgets - Added by Paolo Greppi over 11 years ago

Many thanks, that fixes it.

    (1-4/4)