Search code examples
unit-testinggoogletestgooglemock

How would I mock an object created in the 'method under test'?


I'm pretty deep into unit tests right now and have encountered a rather advanced problem.
There's this class TrackLoader, which has a method called loadTracks(...). I'd like to test that method - without depending on a file system - and this is where it gets tricky

It's a rather complex problem (imho), so maybe some code would be helpful

void TrackLoader::loadTracks(QDomNode tracks, DataManager& dataManager)
{       
    QDomNodeList trackList = tracks.childNodes();

    for(int i = 0; i < trackList.size(); ++i)
    {
      QString filePath = trackList.at(i).toElement().attribute("filePath");

      QSharedPointer<Importer> importer = ImporterFactory::createImporter(filePath);
      importer->parseTrack();
      dataManager.addTrack(filePath, importer->getTrack());          
}

The main problem is, that the actual Importer tries to load the file from the given path. To break file-system-dependency I'd have to mock Importer::parseTrack() and Importer::getTrack to return valid Tracks (without touching the file system).
But I don't see a way to mock the actual Importer. I could mock the ImporterFactory (making ::createImporter non-static), but how would I mock the Importer itself?

Can this be done without refactoring the code itself?


Solution

  • After some fiddling I actually managed to solve the problem.
    Here's the code:

    //this Importer is used to create mockTracks
    std::shared_ptr<MockImporter> datImporter = std::make_shared<MockImporter>();
    EXPECT_CALL(*(datImporter.get()), parseSource()).Times(3);
    EXPECT_CALL(*(datImporter.get()), getSource()).Times(3).WillRepeatedly(Return(mockedTrack()));
    
    //this factory is used to inject the actual MockImporter
    std::shared_ptr<MockImporterFactory> importerFactory = std::make_shared<MockImporterFactory>();
    EXPECT_CALL(*(importerFactory.get()), createImporter(QString("Test1.DAT"))).WillOnce(Return(datImporter));
    EXPECT_CALL(*(importerFactory.get()), createImporter(QString("Test2.DAT"))).WillOnce(Return(datImporter));
    EXPECT_CALL(*(importerFactory.get()), createImporter(QString("Test3.DAT"))).WillOnce(Return(datImporter));
    
    //this injects the MockImporterFactory and runs the code.
    TrackLoaderPtr trackLoader = std::make_shared<TrackLoader>(importerFactory);
    trackLoader->loadTracks(identNode(), _dataManager);
    

    Basically, ImporterFactory was first mocked to ensure that it creates any Importer at all.
    By adding the .WillOnce(Return(datImporter)) bit, I was able to inject a MockImporter through the MockImporterFactory.