DEV Community

Cover image for Writing tests for CLI tool
Emily
Emily

Posted on

Writing tests for CLI tool

This week, I was working on writing tests for my Shinny-SSG project. It was the most challenging lab in the OSD600 course since I had to modify both my code and my project's folder structure to implement the tests.

Set-up

The testing framework that I chose is XUnit. The first reason is that it is trendy compared to another test framework such as NUnits. I created test method stubs from the existing code by Create Unit Tests command. To use it with Xunit, I have to implement the XUnit.net.TestGenerator extension to my project.
Image description

Challenges

I want to test how my tools generated files and folders in the destination with different arguments passed to the program. However, in my old code, I put all the logic of working with arguments in the static int main( string[] args) function. I could not use Interface and Dependency injection to mock the CommandLineApplication because CommandLineUtils does not have an interface for this class. Luckily, I found this guidance from the owner of CommandLineUtils, and he advised that "Split the command-line argument parser and application execution into separate class structures" to test various options programmatically. It is a great suggestion, and I rewrote my program by adding class CommandLineOptions and adding logic to the constructor of class Generator. I can kill two birds with one stone by this change: code refactoring and writing better tests.

Another problem I had was my folder structure. Before, I put the project's sln file, .git file , and src files in the root of the folder. However, when I added a new test project for Shinny-SSG, I had it outside my git folder, and it would be impossible to commit the change and put it in my remote repository. To resolve that, I had to change my folder structure to this:

C:.
├───shinny-ssg
│   ├───bin
│   │   ├───Debug
│   │   │   └───netcoreapp3.1
│   │   │       ├───dist
│   │   │       └───publish
│   │   ├───Destination
│   │   └───Release
│   │       └───netcoreapp3.1
│   ├───obj
│   │   ├───Debug
│   │   │   └───netcoreapp3.1
│   │   └───Release
│   │       └───netcoreapp3.1
│   ├───Properties
│   └───src
└───shinny-ssgTests
    ├───bin
    │   └───Debug
    │       ├───.netcoreapp,version=v3.1  
    │       └───netcoreapp3.1
    ├───obj
    │   └───Debug
    │       ├───.netcoreapp,version=v3.1
    │       └───netcoreapp3.1
    └───src
Enter fullscreen mode Exit fullscreen mode

Testing

I wrote 3 test for Generator class run() function that cover 3 different cases: config file option, input path option and invalid input path option. My tests help uncovering a huge bug in my application. Before, I thought that default keyword was used to specified the default value of a variable.

 cssUrl = cssOption.HasValue() ? cssOption.Value() : default;
Enter fullscreen mode Exit fullscreen mode

However, the default literal is for producing the default value of a type that is null in this case ( CssUrl is the string type).
I also wrote a test that testes the core feature of my application: Given a text and checked if the generated HTML value matched the expected output.
Pull request

Throughout this experience, I learn a lot about software testing and why it is essential for software development. In the future, I will implement more tests for my project and explore other test frameworks.

Discussion (0)