2

Using Visual studio, I'm writing a unit test for a program already written using .Net framework 2.0. All the usual unit tests are working fine except testing of Main (the entry point to the application). The program's Main method is declared without any String[] being passed as argument. The program processes command line arguments using Environment.getCommandLineArgs() in an associated object. The main program looks like following:

[STAThread]
static void Main()
{
        MainProcessor StartProgram = new MainProcessor();
        StartProgram.main();
        StartProgram = null;
}

and command line arguments are processed in main like:

public void main() {
        String [] args = Environment.getCommandLineArgs();
        // process arguments
}

Is there any way to manipulate command line arguments from a test method and processing them using Environment.getCommandLineArgs() as mentioned above?

Bokhari
  • 103
  • 1
  • 2
  • 9
  • 3
    IMO you should not test the main method, but the methods that the main method uses/calls. The functionality that uses the command line parameters should be extracted to classes/methods/functions to which you can pass the command line parameters and then you need to write tests that uses this classes, pass the test parameters from the test method. Your main method should be very very simple and clean. Main is just a method, so you can always call it from the test (`SomeClass.Main(params)`). Have a look at this [article](http://rkennke.wordpress.com/2012/04/12/how-to-test-drive-a-main-method/). – keenthinker Dec 22 '14 at 12:42
  • an option is to use Process object to call this program as mentioned here http://stackoverflow.com/questions/17624134/command-line-arguments-with-visual-studio-unit-testing-c-sharp but that's not very clean for debugging purpose. – Bokhari Dec 22 '14 at 12:47
  • 1
    You should delegate any logic from main to your custom classes in order to test it. Spawning process in unit test to do anything is big red flag. See: http://stackoverflow.com/a/24507984 – k.m Dec 22 '14 at 12:53
  • Thanks @pasty for a nice article. The program is written old-styled without test-driven development approach. The main issue is only about passing command line arguments so that the program interprets them as Environment's command line arguments. I just wanted to see if there's any possibility or if refactoring is the only option. – Bokhari Dec 22 '14 at 13:23
  • I was trying to figure out a similar issue, where I wanted to test Main with normal array args arguments, but it wasn't working. It turned out that the console Program class and Main method weren't set to public... Hopefully that is helpful to someone else stumbling across this question with a similar issue. – ORcoder Jul 06 '22 at 08:25

2 Answers2

2

Note: This is by bad design. MainProcessor (or its main method) shoud take (for example, by constructor) parameters passed from Main arguments.

But still, you can use Fakes from within Visual Studio if you would be able to switch to version 2013:

  1. In your unit test project, from References, right click System and select Add Fakes Assembly
  2. In the new file mscorlib.fakes which will be created, add in section Fakes:

    <ShimGeneration>
      <Add FullName="System.Environment"/>
    </ShimGeneration>
    
  3. Rebuild

  4. In your test you can stub Environment static method calls:

    [TestMethod]
    public void TestSomeMethod()
    {
        using (ShimsContext.Create())
        {
            ShimEnvironment.GetCommandLineArgs = () => new string[] { "arg1", "arg2" };
    
            // Your test here.
         }
    }
    

You can look at this article also.

Regarding Visual Studio 2010, you can use Fakes predecessor named Moles, which is very similar to the above one. Just add it Visual Studio 2010 Moles from Extension Manager. I believe in Moles it will be:

[TestMethod]
public void TestSomeMethod()
{
    using (MolesContext.Create())
    {
        Moles.MEnvironment.GetCommandLineArgs = () => new string[] { "arg1", "arg2" };

        // Your test here.
     }
}
Konrad Kokosa
  • 16,563
  • 2
  • 36
  • 58
1

Rather than pulling the command-line parameters from Environment, why not just take them as arguments to Main?

    static void Main(string[] args)
    {
    }

Now pass those arguments in to your MainProcessor's constructor as well.

    static void Main(string[] args)
    {
        var processor = new MainProcessor(args);
        processor.main();
    }

If that's all your Main method does, then you can focus your testing on MainProcessor instead. You can also just invoke Program.Main, passing in whatever you want, but since the only thing you can observe is whether or not an exception is thrown, it's not terribly useful as a test.

If, for some reason, the standard handling of "args" were to mysteriously stop working, you have bigger problems... we all do.

Mel
  • 2,337
  • 19
  • 25
  • 1
    I wish, I could. But actually, this program was written 6 years ago and has a structure such that the command line inputs are being processed 3 levels down the call of main (From Main -> processor.main() -> pre_processor.processCommandLineArgs() and this function processes command line arguments from Environment. – Bokhari Dec 22 '14 at 15:51