DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Using Unblocked to Fix a Service That Nobody Owns
  • Solid Testing Strategies for Salesforce Releases
  • Running and Debugging Tests With Playwright UI Mode
  • Working With Vision AI to Test Cloud Applications

Trending

  • Unlocking Data with Language: Real-World Applications of Text-to-SQL Interfaces
  • Concourse CI/CD Pipeline: Webhook Triggers
  • Cookies Revisited: A Networking Solution for Third-Party Cookies
  • Immutable Secrets Management: A Zero-Trust Approach to Sensitive Data in Containers
  1. DZone
  2. Data Engineering
  3. Data
  4. Isolating IntelliJ Plugin Tests Using Temporary Project Directories

Isolating IntelliJ Plugin Tests Using Temporary Project Directories

Make your IntelliJ plugin tests more robust using temporary project directories.

By 
Markus Zimmermann user avatar
Markus Zimmermann
·
Feb. 21, 23 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
2.7K Views

Join the DZone community and get the full member experience.

Join For Free

When testing a plugin for IntelliJ-based IDEs using a UI test, the plugin gets to operate within a fully-featured instance of the IDE. Most tests will therefore require a project to perform their actions on. This opens a question: How do you deal with modifications to test projects made by tests? Add undo logic at the end of every test? Revert using a version control system? Those options sound like easy ways to make new mistakes.

We faced the same problem when writing tests for our own plugin, Symflower, for IntelliJ IDEA and GoLand, but we’ve opted for a simpler solution. We don’t run tests on the canonical source of our test projects but rather copy the entire project directory and use that copy instead. This avoids relying on repetitive cleanup logic or VCS-based reverting that could undo legitimate changes to the source files.

Setting up Temporary Project Directories To Isolate IntelliJ Plugin Tests

We’ve written a class that encapsulates implementation details, such as the base directory for temporary project copies, and handles the translation of relative paths.

Java
 
package com.symflower.testing.ui;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.commons.io.FileUtils;

/** TestProject is a utility class to provide a temporary project copy and a cleanup method to be used after a test run. */
public class TestProject {
    private File projectPath;

    public TestProject(String name) {
        var sourcePath = "test-projects/" + name; // TODO Replace with your own path for test projects.
        // Copy the source path to a temporary directory where all the tests are run.
        try {
            projectPath = Files.createTempDirectory(Path.of("tmp-project-copies"), name).toFile(); // TODO Replace with your own base directory for temporary copies of projects.
            projectPath.deleteOnExit();
            FileUtils.copyDirectory(new File(sourcePath), projectPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /** getProjectName returns the project's name. */
    public String getProjectName() {
        return projectPath.getName();
    }

    /** getProjectPath returns the path to the project's temporary directory. */
    public String getProjectPath() {
        return projectPath.getPath();
    }

    /** getFilePath returns the given relative file path prefixed with the project's directory path. */
    public String getFilePath(String relativeFilePath) {
        return Path.of(projectPath.getPath(), relativeFilePath).toString();
    }

    /** cleanup deletes the temporary directory of the project. */
    public void cleanup() {
        try {
            FileUtils.deleteDirectory(projectPath);
        } catch (IOException e) {}
    }
}


The following example shows you how you can use the class in your test code:

Java
 
@Test
void testSomething() {
    // REMARK "editor" and "system" are helpers defined in the test suite.
    var project = new TestProject("test-plain");
    editor.openProject(project.getProjectPath());
    system.assertFileExists(project.getFilePath("plain.java"));
}


After the test, call the cleanup method on instances of TestProject to get rid of the temporary project directory. We’ve automated that further by defining a base class for all of our UI tests that lets tests provide the instance of TestProject that represents the currently active project and cleans up the project when the test exits, regardless of its outcome.

Java
 
package com.symflower.testing.ui;

import static com.intellij.remoterobot.stepsProcessing.StepWorkerKt.step;

import com.intellij.remoterobot.RemoteRobot;
import com.intellij.remoterobot.utils.Keyboard;
import com.symflower.testing.ui.helpers.EditorHelper;
import com.symflower.testing.ui.helpers.SymflowerHelper;
import com.symflower.testing.ui.helpers.SystemHelper;
import com.symflower.testing.ui.utils.RemoteRobotExtension;
import com.symflower.testing.ui.utils.StepsLogger;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.ExtendWith;

/** BaseUITest is a base class for UI test suites that operate on a project. */
@ExtendWith(RemoteRobotExtension.class)
public abstract class BaseUITest {
   // Provide basic tools to interact with the testing instance.
   protected final RemoteRobot remoteRobot = new RemoteRobot("http://127.0.0.1:8082");
   protected final Keyboard keyboard = new Keyboard(remoteRobot);

   // Provide higher-level helpers.
   protected final EditorHelper editor = new EditorHelper(remoteRobot);
   protected final SymflowerHelper symflower = new SymflowerHelper(remoteRobot);
   protected final SystemHelper system = new SystemHelper();

   /** cleanup is a function which is run after each test to reset settings and perform special cleanups which is also called if a test failed. */
   protected Runnable cleanup;

   /** project holds the "TestProject" instance for a test. */
   protected TestProject project;

   @BeforeAll
   public static void testSetup() throws IOException {
       StepsLogger.init();
   }

   @AfterEach
   public void cleanUp(final RemoteRobot remoteRobot) {
       // The try/finally block ensures that project-level cleanups always run even if the test-level cleanup function throws.
       try {
           step("Run cleanup task", () -> {
               if (cleanup != null) {
                   cleanup.run();
               }
           });
       } finally {
           cleanup = null; // Reset the cleanup function to avoid leaking in tests which do not define one.

           editor.closeProject();

           if (project != null) {
               project.cleanup();
               project = null;
           }
       }
   }
}


Feel free to use these in your own projects using the MIT license. If you need help writing your tests, try the Symflower plugin for yourself.

Integrated development environment MIT License UI Directory intellij Testing Data Types

Published at DZone with permission of Markus Zimmermann. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Using Unblocked to Fix a Service That Nobody Owns
  • Solid Testing Strategies for Salesforce Releases
  • Running and Debugging Tests With Playwright UI Mode
  • Working With Vision AI to Test Cloud Applications

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!