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 Jan Hrubeš over 11 years ago
Hi,
try this wt test environment .
Jan
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