4

I would like to create a test suite for my C++ wxWidgets app and am having trouble with figuring out how to test GUI components. The article in the docs about how to write unit tests is written from the perspective of augmenting the existing unit tests but doesn't address how to start new ones.

I was able to create a test suite using Catch (which appears to be the recommended approach) but am having trouble getting started on the GUI side.

On the non-GUI side, I created a test that looks like this

#include "catch.hpp"

#include "wx/wx.h"

TEST_CASE("wx", "[wx]") {
    wxPoint p = wxPoint(10, 10);
    REQUIRE(p.x == 10);
}

and a CMakeLists file that looks like this (just the relevant section):

set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wxWidgets/3rdparty/catch/include/)
add_library(Catch INTERFACE)
target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})

# Make test executable
set(TEST_SOURCES 
    test.cpp
)
add_executable(tests ${TEST_SOURCES})
target_link_libraries(tests Catch ${wxWidgets_LIBRARIES})

And this worked just fine. So there is no problem linking against the wxWidgets libraries. I believe the problem is in creating a wxApp or an event loop. But I'm unsure of how to rectify that.

When I create a test that attempts to create a button (like in buttontest.cpp that is suggested in the docs to use as an example), I receive a Segmentation violation signal.

TEST_CASE("wx", "[wx]") {
    wxButton* button = new wxButton(wxTheApp->GetTopWindow(), wxID_ANY, "wxButton");
    wxYield();
}

Is there a place that demonstrates how to create GUI tests for wxWidgets from scratch instead of just adding to the wxWidgets tests themselves?

DHamrick
  • 8,338
  • 9
  • 45
  • 62

2 Answers2

4

Writing GUI tests is unfortunately not very simple, but can be done. The basic idea is that you use wxUIActionSimulator to trigger the user action that you want to test and then check that the UI has been updated as expected, which often involves calling wxYield() to let all the events be processed. wxWidgets own GUI tests themselves show how to do it, but also that there are often some additional complications, just search for wxUIActionSimulator under tests to see many example of its use.

You can also look at this test to see an example of how you can use wxTEST_DIALOG to test actions resulting in modal dialog, that would normally block the GUI and prevent your program from continuing.

VZ.
  • 21,740
  • 3
  • 39
  • 42
  • I just wanna point out that what you are talking about is not Unit Test. You are talking about End-to-End tests - and as far as I understand, that is not what OP asked about. – Refael Sheinker Nov 05 '21 at 12:45
0

Please refrain from wiring Unit Tests for GUI, this goes completely against the rules of Clean Code. There are several reasons for it:

  1. Most of the methods in the various GUI frameworks (including) wxWidgets are private (and rightly so). And the rules of Clean Code say we should not write Unit Tests for private methods - they are simply implementation details.
  2. Developing GUI application requires seperation of View (GUI) Logic and Business (for example: data base access) Logic. The best way to do it is to use the MVVM design pattern.

Also, I would suggest a good book: Clean Code. Its THE book on Clean Code. It also touches on TDD - Test Driven Development - which is also an important subject strongly related to your question.

Following @VZ's comments, I wanna add the following differences between Unit Tests, Integration Tests, and End-to-End tests:

  • Unit tests - always use mock. The "unit" of test is a method.
  • Integration tests - never use mock. The "unit" of test is a class.
  • End-to-end tests - uses the actual program. The "unit" of test is a single "happy path".
Refael Sheinker
  • 713
  • 7
  • 20
  • 2
    I agree - business logic should be separated and tested on its own. However, I also have created custom UI elements that need testing as well. I want to make sure that as I modify custom elements I am not breaking their behavior. – DHamrick Nov 04 '21 at 08:12
  • @DHamrick Custom GUI elements, are just like any other pieces of GUI. They should be written with MVVM in mind and then the Unit Testing becomes obvious. Let me give you an example: lets say you have a custom GUI widget that displays rows from data base. The class responsible for getting data form data base and parsing it and preprocessing it and what not, needs to be separated from the custom widget control (e.g., injected as a parameter to the ctor of the custom widget control). – Refael Sheinker Nov 04 '21 at 08:42
  • 3
    This is a pretty bad answer. You should have tests for your business logic, but you should _also_ have tests for your GUI, there is absolutely no reason to "refrain" from writing such tests. – VZ. Nov 05 '21 at 12:15
  • @VZ "there is absolutely no reason to "refrain" from writing such tests." --> Yes, there is. Please see my answer, point number 1. But, maybe I understand why we are in disagreement: I was talking about Unit Tests and you are talking about End-to-End tests. I will promptly rephrase my answer to be more precise. You are correct. Thank you. – Refael Sheinker Nov 05 '21 at 12:49
  • 1
    This is narrow-viewpoint nonsense. Your definitions of different types of testing are examples of such types of testing, not exclusive. A unit test is simply a test for the smallest testable parts of an application. That *may* be a method, but is not necessarily so. A single method typically has multiple unit tests (for valid and invalid inputs). A test that, say, verifies a particular event is emitted when a mouse click is performed on a column header in a custom control would be a unit test, *not* an "end-to-end" test, and 100% a valid thing to want to automate testing for. – Michael Kirkham Aug 07 '22 at 10:22
  • Further, this doesn't do anything to answer the OP's question. – Michael Kirkham Aug 07 '22 at 11:42