c++qtqfiledialogqttest

How to control a QFileDialog using Qt Test?


I have 2 questions:

  1. How can I access a QFileDialog and write the path of a file in the "File name" field using the Qt Test module?

    I am asking that because I am developing some GUI tests in Qt and now I need to open a text file. The following code creates the QFileDialog and gets the file path:

    QString filePath = QFileDialog::getOpenFileName(
                                this,
                                "Open",
                                "",
                                tr("Text Files (*.txt)") );
    
  2. If I am using a thread (QThread) to load the content in this text file, how can I wait for this thread to finish in my GUI tests?

    I know I can use the QTest::qSleep( milliseconds ); command, but I don't think it is a good practice in this case.

If possible show me an example, please.


Solution

    1. Unfortunately, it's not possible using the Qt Test module. You have several choices:

      1. Add test hooks that bypass that dialog: you need to instrument your code to make it testable. You could e.g. set a testFile property on the object that asks for the file to a file path, if the property is set the object can skip asking for the file.

        const char k_testFile[] = "k_testFile";
        
        MyClass::foo() {
          ...
          auto testFile = property(k_testFile);
          auto filePath = testFile.isNull()
            ? QFileDialog::getOpenFilePath(...)
            : testFile.toString();
          ...
        }
        
      2. Use a non-native dialog and then it's a normal widget that you can test using Qt Test.

      3. Use platform-specific means of finding the native dialog and interacting with it. You'd need to implement it for every platform you intend to test on.

    2. You should be emitting a signal after the file has been loaded. The test can wait for that signal. You don't even need to use an explicit thread to load the file, an idiomatic way of asynchronously loading a file is:

      QString filePath = ...;
      QtConcurrent::run(ioPool, [this, filePath]{
        auto data = MyLoader::load(filePath);
        emit haveFileData(data);
      });
      

      The class where you invoke this code should have a haveFileData signal that some other code connects to and proceeds as desired. The ioPool is the thread pool used for I/O bound operations, it could be QThredPool::globalInstance() if you want the global pool to be the I/O pool. The CPU- and I/O-bound thread pools should be separate.