Writing tests for CLI tool
Tags: cshap, testing, opensource
Published: 2021-11-14 18:27:00

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](https://xunit.net/). 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](https://docs.microsoft.com/en-us/visualstudio/test/create-unit-tests-menu?view=vs-2022) command. To use it with Xunit, I have to implement the XUnit.net.TestGenerator extension to my project. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hx3nbipf2axkmrpzmeh4.png)

## 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](https://github.com/natemcmaster/CommandLineUtils/issues/279) 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](https://github.com/hphan9/shinny-ssg/blob/main/shinny-ssg/src/CommandLineOptions.cs) 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:
```bash
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
```
## 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.
```C#
 cssUrl = cssOption.HasValue() ? cssOption.Value() : default;
```
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](https://github.com/hphan9/shinny-ssg/commit/23c766a759bc52f1889d78c578da2b29cbb0a248#diff-765ec9ae5392385dc6cb44e7fb9af38ebf78e87a463546cfd86d7d42590341e3)

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.