Symflower for IntelliJ IDEA

Installation and Usage

  1. Download

    Open your IDE and click on File > Settings...

    Click on File, then Settings.

    Select the Plugins category. Then type "symflower" into the search field and click the "Install" button of the Symflower plugin.

    Select the Plugins category. And search for symflower and press the Install button.

    After the installation process is finished you might see a "Restart IDE" button where the "Install" button was before. If this is the case please press the "Restart IDE" button. Then select the "Restart" button on the appearing popup window. Now you just have to wait until your IDE is restarted for the Symflower plugin to work.

    If no "Restart IDE" button appears you should be already good to go. Just close the settings window and start generating tests with Symflower. In case that the plugin is not working please restart the IDE yourself.

    Restart IntelliJ by clicking the Restart IDE button.

    Please check out our End-user License Agreement and our Privacy Policy.

  2. Generate Tests

    Open the file you want to analyze, right click into a function and select “Symflower: Generate Tests for Function”. A new generated test file should appear.

  3. Give Us Feedback

    We encourage you to send us your feedback on how you like Symflower. If you are missing a feature or found a problem that you think should be fixed, please open an issue on our community issue tracker or write us an email at hello@symflower.com . Subscribe to the Symflower newsletter to receive updates on new features, use cases and topics on software development.

Tutorial

  1. The Scenario

    In the next minutes you will learn how to generate high-coverage unit test suites with Symflower whenever you change your source code. Symflower gives you rapid feedback if your implementation is on the right track, and all that while you are developing new features and fixing bugs.

    Our specification for the task of this tutorial is to create a simple copy function that copies an array of strings from the parameter “from” to the parameter “to”, and then returns “to” such that callers can use the function in an expression.

  2. Creating an API

    We have defined the basic behavior of the function, which allows us to already define its API as a stub. Create a directory “tutorial” and create a file “Copy.java” in the directory with the following content:

    class Copy {
        static String[] copy(String[] from, String[] to) {
            return to;
        }
    }
    

    As you can see, our first iteration of the function returns “to” as defined in the specification. The copying functionality is not yet implemented. However, since we now have compilable source code, we can already generate our first unit test with Symflower.

  3. Generating Our First Unit Test

    Let’s generate unit tests for the source code we already have. If you do not have Symflower installed, please take a look at the installation guide first. Next, right click in the editor of the “Copy.java” file and select “Symflower: Generate Tests for Function”.

    Congratulations, you just generated your first unit test with Symflower! The file tree shows you that a new file “CopySymflowerTest.java” has been created with the following content:

    import org.junit.jupiter.api.*;
    import static org.junit.jupiter.api.Assertions.*;
    
    public class CopySymflowerTest {
        @Test
        public void copy1() {
            String[] from = null;
            String[] to = null;
            String[] actual = Copy.copy(from, to);
    
            assertNull(actual);
        }
    }
    

    This test case represents the simplest unit test for our function: using “null” for the parameters “from” and “to” which lets the function return “null”. We can now execute this unit test and see that it fully covers our source code, which is what Symflower is always aiming for when it generates unit tests.

    Looking at the specification we notice that the case where an input is null is not defined. Therefore, this might be a test case that should go away with a complete implementation of the specification.

  4. Implementing the Specification

    Next we can implement the actual copy functionality of the specification. There are multiple options how one could accomplish this task. We decided to go with the simplest one where we copy elements one by one. This code is easy to read and understand and works the same way in most languages:

    class Copy {
        static String[] copy(String[] from, String[] to) {
            for (int i = 0; i < from.length; i++) {
                to[i] = from[i];
            }
    
            return to;
        }
    }
    

    Let’s have Symflower update our generated tests. Right click and select “Symflower: Generate Tests for Function” once again. This should yield 5 unit tests. Let’s have a closer look.

    There are 3 unit tests that fail because the function throws an exception and 2 that pass. Let’s look at the passing tests first. We want to know if what already perfectly works is what we want:

    @Test
    public void copy4() {
    	String[] from = { null };
    	String[] to = { null };
    	String[] expected = { null };
    	String[] actual = Copy.copy(from, to);
    
    	assertArrayEquals(expected, actual);
    }
    

    We copy “from” with a single “null” element into “to” with a single “null” element. The expected return argument is an array with a single “null” as its element. This is definitely according to our specification. Let’s look at the other passing test.

    @Test
    public void copy2() {
    	String[] from = {};
    	String[] to = null;
    	String[] actual = Copy.copy(from, to);
    
    	assertNull(actual);
    }
    

    We copy “from” with no elements into “to” which is null and return null. Hold on, our specification says that we are copying from one array into another. Hence, we need to check that our parameters must be not null.

  5. Validating Our Inputs

    When one of our parameters “from” or “to” is null, we found an invalid usage of our API. Java’s “IllegalArgumentException” is made for cases like this. Let’s add it to our code:

    class Copy {
        static String[] copy(String[] from, String[] to) {
            if (from == null || to == null) {
                throw new IllegalArgumentException();
            }
    
            for (int i = 0; i < from.length; i++) {
                to[i] = from[i];
            }
    
            return to;
        }
    }
    

    Generating new tests gives us 5 tests once again. The unit test with the empty strings as elements in “to” and “from” still exists, but the other unit tests have changed.

    Now there are two unit tests that exercise our new validation logic:

    @Test
    public void copy1() {
    	String[] from = null;
    	String[] to = null;
    	assertThrows(IllegalArgumentException.class, () -> {
    		Copy.copy(from, to);
    	});
    }
    
    @Test
    public void copy2() {
    	String[] from = {};
    	String[] to = null;
    	assertThrows(IllegalArgumentException.class, () -> {
    		Copy.copy(from, to);
    	});
    }
    

    Additionally, we have a unit test that has empty arrays as parameters and returns an empty array:

    @Test
    public void copy3() {
    	String[] from = {};
    	String[] to = {};
    	String[] expected = {};
    	String[] actual = Copy.copy(from, to);
    
    	assertArrayEquals(expected, actual);
    }
    

    Perfect, so far everything is either according to our specification or throws an “IllegalArgumentException”. Let’s look at one more generated unit test:

    @Test
    public void copy5() {
    	String[] from = { "" };
    	String[] to = {};
    	// assertThrows(ArrayIndexOutOfBoundsException.class, () -> {
    	Copy.copy(from, to);
    	// });
    }
    

    Symflower has found an out-of-bounds exception since we are copying from an array with 1 element to an array with 0 elements.

    What we should do in this case is not defined in the specification: we could either again throw an “IllegalArgumentException” if the parameters are of different size, we could allocate a new array with the correct size or we could copy just the elements that fit in the “to” array. However, the best solution is to talk to our fellow developers, our team, and clarify the specification. After that we can adapt the code and use Symflower to make sure that everything works as expected, and we didn’t forget any edge cases.

  6. What Just Happened?

    You just learned how to generate high-coverage unit test suites with the Symflower plugin for IntelliJ IDEA and how to use these tests to efficiently and effectively implement a specification. Additionally, you learned how to take advantage of generated unit tests as rapid feedback to validate your implementation as well as debug and fix emerging problems.

    At Symflower we see this workflow as the next evolutionary step of TDD (Test Driven Development). Instead of painfully defining all test cases for what we want to implement, we can finally focus on the implementation itself. Everything else is covered by Symflower, which gives us confidence and rapid feedback for every change we make.

  7. Next Steps

    You now know how to generate unit tests for your source code with Symflower. Next, you can explore additional functionality by reading the plugin’s marketplace page, and look at further examples and scenarios on the Symflower blog, the tutorials repository and the examples repository.

    Most importantly, we encourage you to send us your feedback on how you liked the tutorial and Symflower. If you are missing a feature or found a problem that you think should be fixed, please open an issue on our community issue tracker or write us an email at hello@symflower.com . Subscribe to the Symflower newsletter to receive updates on new features, use cases and topics on software development.