1

At the moment, I have created a test project using a project template from Visual Studio Unit Test App (Winui 3) and my code looks like this

namespace TestMyWinrtApp
{
    TEST_CLASS(CppUnitTests)
    {
    public:
        TEST_METHOD(CppTestOne)
        {
            try {
                auto bp = winrt::make< winrt::MyUI::implementation::BlankPage>();
                Button btn = bp.FindName(L"myButton").as<winrt::Microsoft::UI::Xaml::Controls::Button>();
                winrt::hstring btnTag = btn.Tag().as<winrt::hstring>();
                Assert::IsTrue(btnTag == L"MyTag");
            } catch (const winrt::hresult_error& ex) {
                MessageBox(
                    NULL,                  
                    ex.message().c_str(), 
                    L"Error", 
                    MB_OK | MB_ICONINFORMATION
                );
            }
        }
    };
}

However, after starting the tests, I get the exception: "The Application Called An Interface that was Marshalled for a Different Thread". As far as I know, this exception thrown is due to the fact that I try to create an winrt::make< winrt::MyUI::implementation::BlankPage>() UI element outside the UI thread. I found a solution for C# which is to add an [UITestMethod] attribute for the test method. But how i can do unit tests like this in C++ test project?

14
  • Should the test be written in c++-cli? Commented Jun 9, 2024 at 13:52
  • @πάνταῥεῖ no, i want to use standard c++17 Commented Jun 9, 2024 at 13:56
  • Well, then the c# code might be hard to adapt for this. Commented Jun 9, 2024 at 13:57
  • @πάνταῥεῖ in general, i am interested in any c++ solution Commented Jun 9, 2024 at 13:58
  • You're probably better off looking up c++-cli, since that might (should) support that [UITestMethod] as well and easy as with c#. Commented Jun 9, 2024 at 14:29

1 Answer 1

0

The Problem is solved. As @Simon Mourier & @György Kőszeg suggested in comments, we can We can add a task to the ui thread queue to use the ui elements there. To access the ui thread queue, I used a global variable because I did not find any other way to access the ui thread queue other than through a global variable whose value is assigned in the OnLaunched method in the App class after activating the main window. Finally, solution looks like this:

#include <winrt/base.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
using namespace winrt::Microsoft::UI::Xaml::Controls;
extern winrt::Microsoft::UI::Dispatching::DispatcherQueue UIDispatcherQueue;

TEST(UITEST, first)
{
    std::promise<bool> p;
    std::future<bool> f = p.get_future();
    bool enqueued = UIDispatcherQueue.TryEnqueue(winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority::High, [&p]()
    {
        auto bp = winrt::make< winrt::WinUIGtest::implementation::BlankPage>();
        Button btn = bp.FindName(L"myButton").as<Button>();
        winrt::hstring btnTag = btn.Tag().as<winrt::hstring>();
        bool isEquals = (btnTag == L"MyTag");
        p.set_value(isEquals);
    });

    bool res = f.get();
    ASSERT_EQ(res, true);
}

As you can see, I was also changed test framework to google test. If you want to do so, then you need:

  1. Install google test in your project
  2. Run google tests manually (you can do it in the App::OnLaunched method)

Note: If you want to run tests which should enqueue callback in the UI thread, you should run tests in the separate thread, otherwise this action seems like produced deadlock.

    // allocate console to see gtest output
    if (AllocConsole()) {
        FILE* pStdout, * pStderr;
        freopen_s(&pStdout, "CONOUT$", "w", stdout);
        freopen_s(&pStderr, "CONOUT$", "w", stderr);
    }
    
    // run gtest in separate thread
    std::thread([]
    {
        int argc = 1;
        char** fakeArgv = new char* [1];
        char* path = new char[1024];
        strcpy_s(path, 1024, R"(C:\Users\UserName\source\repos\WinUIGtest\x64\Debug\WinUIGtest\WinUIGtest.exe)");
        fakeArgv[0] = path;
        ::testing::InitGoogleTest(&argc, fakeArgv);
        //::testing::InitGoogleMock(&argc, fakeArgv);
    
        RUN_ALL_TESTS();
    }).detach();
    
    // disable standard tests
    //winrt::Microsoft::VisualStudio::TestPlatform::TestExecutor::WinRTCore::UnitTestClient::Run(m_args);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.