whyUs/inANutshell.svg

Generate unit tests on the go

Symflower integrates into your development workflow by generating high-coverage unit test suites while you are coding. Find bugs and debug corner cases right away and without effort.

whyUs/inANutshell.svg

You take care of your code
We take care of your unit tests

Installation and Usage

  1. Download

    Execute the following command which downloads and installs "symflower":

    curl -sSLf https://get.symflower.com/install | sudo sh

    Execute the following command which downloads the binary to `/usr/local/bin/symflower`:

    sudo curl -SLf -o /usr/local/bin/symflower https://download.symflower.com/local/latest/symflower-linux-x86_64

    Make the binary executable:

    sudo chmod +x /usr/local/bin/symflower

    Verify the installation by querying the version of the binary:

    symflower --version

    Install the Windows Subsystem for Linux (WSL2) using the official installation documentation.

    Execute the following command which downloads and installs "symflower":

    curl -sSLf https://get.symflower.com/install | sudo sh
  2. Generate Tests

    Change the directory to the workspace you want to analyze and run the following command:

    symflower
    

    Each source code file receives a generated test file.

    You can also generate tests for single directories and files instead of the whole workspace:

    symflower src/some/directory src/some/file.go src/some/file.java
    
  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”, change the working directory to the directory and create a file “Copy.java” 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, simply execute the following command in our “tutorial” directory:

    symflower
    

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

    import org.apache.commons.lang3.builder.EqualsBuilder;
    import org.junit.*;
    import static org.junit.Assert.*;
    
    public class CopySymflowerTest {
        @Test
        public void copy1() {
            java.lang.String[] from = null;
            java.lang.String[] to = null;
            java.lang.String[] expected = null;
            java.lang.String[] actual = Copy.copy(from, to);
    
            assertTrue(EqualsBuilder.reflectionEquals(expected, actual, false, null, true));
        }
    }
    

    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 ask Symflower again to generate unit tests with the same command as before:

    symflower
    

    Symflower has generated 5 unit tests for us. 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 copy3() {
        java.lang.String[] from = { null };
        java.lang.String[] to = { "" };
        java.lang.String[] expected = { null };
        java.lang.String[] actual = Copy.copy(from, to);
    
        assertTrue(EqualsBuilder.reflectionEquals(expected, actual, false, null, true));
    }
    

    We copy “from” with a single “null” element into “to” with a single empty string. 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() {
        java.lang.String[] from = {  };
        java.lang.String[] to = null;
        java.lang.String[] expected = null;
        java.lang.String[] actual = Copy.copy(from, to);
    
        assertTrue(EqualsBuilder.reflectionEquals(expected, actual, false, null, true));
    }
    

    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;
        }
    }
    

    Running Symflower 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 // (expected = java.lang.IllegalArgumentException.class)
    public void copy1() throws java.lang.IllegalArgumentException {
        java.lang.String[] from = null;
        java.lang.String[] to = null;
        java.lang.String[] actual = Copy.copy(from, to);
    }
    
    @Test // (expected = java.lang.IllegalArgumentException.class)
    public void copy2() throws java.lang.IllegalArgumentException {
        java.lang.String[] from = {  };
        java.lang.String[] to = null;
        java.lang.String[] actual = 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() {
        java.lang.String[] from = {  };
        java.lang.String[] to = {  };
        java.lang.String[] expected = {  };
        java.lang.String[] actual = Copy.copy(from, to);
    
        assertTrue(EqualsBuilder.reflectionEquals(expected, actual, false, null, true));
    }
    

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

    @Test // (expected = ArrayIndexOutOfBoundsException.class)
    public void copy5() {
        java.lang.String[] from = { "" };
        java.lang.String[] to = {  };
        java.lang.String[] actual = 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 Symflower 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 showing the help with symflower --help, 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.

Key Benefits

High Coverage

Symflower generates high-coverage unit tests to validate your code. Each execution path and problem gets covered by a dedicated unit test.

local/unitTestCoverage.svg
local/rapidFeedback.svg

Rapid Feedback

Symflower gives you rapid feedback for your code, before you push your changes. Iterate faster on emerging problems and find them before they hit production.

local/rapidFeedback.svg

Reliable Reproducers

Symflower provides unit tests that are deterministic, minimal and just like you would write them. The perfect reliable reproducer to debug and fix problems.

local/reproducers.svg
Looking for Symflower Server?

Looking for Symflower Server?


Features erkunden

Multi Language Support

Symflower comes with support for multiple programming languages. New programming languages are regularly added and are automatically enabled for everyone. Contact us at hello@symflower.com in case your programming language is not yet on our roadmap.

Coming soon
Java
Java
Go
Go

Coming soon

C++
C++
C#
C#
JavaScript
JavaScript
Java
Java
Go
Go
C++
C++
C#
C#
JavaScript
JavaScript