2016-04-14 48 views
2

我有一些功能,这取决于命令行参数,不同的参数应导致不同的结果。单元测试中的“模拟”命令行参数

我不能直接“模拟”这个参数,因为存在某种链依赖性 - 我需要单元测试一些xaml控件,这取决于视图模型,这取决于某些额外的类,它提取命令行参数使用Environment.GetCommandLineArgs,并且我不能直接影响最后一个类来手动设置参数,而不是使用GetCommandLineArgs

所以,我想知道,是否有任何方法可以使Environment.GetCommandLineArgs返回值,我希望它返回,对于某些单元测试。

+1

你可以发布你到目前为止尝试过的代码吗? – ManoDestra

+0

你有没有尝试过使用Moq?你可以使任何方法返回任何你想要的。 – Wjdavis5

+0

@ Wjdavis5我尝试模拟实际的类而不是接口,但尝试过不好的经验。但是,一些不同的解决方案来了,改变了逻辑我实际上是单元测试,可以应用moq来。 – lentinant

回答

2

你需要抽象Environment.GetCommandLineArgs或什么都最终调用它背后的东西,你可以嘲笑

public interface ICommandLineInterface { 
    string[] GetCommandLineArgs(); 
} 

能最终在一个具体的类来实现类似

public class CommandInterface : ICommandLineInterface { 
    public string[] GetCommandLineArgs() { 
     return Environment.GetCommandLineArgs(); 
    } 
} 

,并可以使用进行测试Moq and FluentAssertions

[TestMethod] 
public void Test_Should_Simulate_Command_Line_Argument() { 
    // Arrange 
    string[] expectedArgs = new[] { "Hello", "World", "Fake", "Args" }; 
    var mockedCLI = new Mock<ICommandLineInterface>(); 
    mockedCLI.Setup(m => m.GetCommandLineArgs()).Returns(expectedArgs); 
    var target = mockedCLI.Object; 

    // Act 
    var args = target.GetCommandLineArgs(); 

    // Assert 
    args.Should().NotBeNull(); 
    args.Should().ContainInOrder(expectedArgs); 

} 
+0

非常好的主意! +1 –

+0

最后,我做了这件事。 – lentinant

1

既然你正在处理环境变量,为什么我们不把外部的依赖包装到一个EnvironmentHelper类中,然后注入依赖?

这里是我的建议:

public class EnvironmentHelper 
{ 
    Func<string[]> getEnvironmentCommandLineArgs; 

     // other dependency injections can be placed here 

     public EnvironmentHelper(Func<string[]> getEnvironmentCommandLineArgs) 
     { 
      this.getEnvironmentCommandLineArgs = getEnvironmentCommandLineArgs; 
     } 

     public string[] GetEnvironmentCommandLineArgs() 
     { 
      return getEnvironmentCommandLineArgs(); 
     } 
} 

这里是模拟方法:

public static string[] GetFakeEnvironmentCommandLineArgs() 
{ 
    return new string[] { "arg1", "arg2" }; 
} 

在你的源代码:

EnvironmentHelper envHelper = new EnvironmentHelper(Environment.GetCommandLineArgs); 
string[] myArgs = envHelper.GetEnvironmentCommandLineArgs(); 

在你的单元测试代码:

EnvironmentHelper envHelper = new EnvironmentHelper(GetFakeEnvironmentCommandLineArgs); 
string[] myArgs = envHelper.GetEnvironmentCommandLineArgs(); 
+0

@Nkosi感谢您的编辑。我删除了我最后的评论,因为我不想引起其他读者的困惑。 –

0

如果你想要一些单元可测试的东西,它应该对一个抽象至少和它的实现一样严格。

通常你会通过你的类的构造函数或属性方法来获得依赖关系。一般来说,构造函数是首选,因为现在您的类的使用者在编译时知道需要哪些依赖关系。

public void int Main(string[] args) 
{ 
    // Validate the args are valid (not shown). 

    var config = new AppConfig(); 
    config.Value1 = args[0]; 
    config.Value2 = int.Parse(args[1]); 
    // etc.... 
} 

public class MyService() 
{ 
    private AppConfig _config; 

    public MyService(AppConfig config) 
    { 
     this._config = config; 
    } 
} 

我通常不把配置对象放在接口后面,因为它只有数据 - 这是可序列化的。只要它没有方法,那么我就不需要用override -d行为替换它。我也可以直接在我的测试中使用new

另外,我从来没有遇到过一种情况,当我想依靠抽象的命令行参数本身到一个服务 - 为什么它需要知道它在命令行后面?我得到的最接近的是使用PowerArgs来轻松解析,但我会在Main中使用该权利。我通常做的事情就像是在命令行参数中读取Web服务器的端口号(我让应用程序的用户选择,以便我可以在同一台计算机上运行多个Web服务器副本 - 可能不同版本等我可以运行自动化测试,而我正在调试,而不是冲突端口),直接在我的Main类解析它们。然后,在我的Web服务器中,我依赖于解析的命令行参数,在这种情况下是int。这样,配置来自命令行的事实是无关紧要的 - 如果我愿意,我可以稍后将它移动到App.config文件(它也基本上绑定到进程的生命周期) - 然后我可以将常见配置提取到configSource文件。

通常将命令行和依赖关系抽象为一个强类型对象,而不是依赖于命令行的抽象(每个服务将消耗必须重新解析,如果保持纯的话) - 也许是应用级配置类和测试级配置类,并根据需要引入多个配置对象 - (应用程序不一定会在意这一点,而E2E测试基础架构需要在App.config的单独部分中使用这个:我从哪里获取客户端静态文件,在哪里获取测试或开发人员环境中的构建脚本以自动生成/自动更新index.html文件等)。

0

你可以用Typemock Isolator更容易做到这一点。 它允许不仅模拟接口,所以。请看:

[TestMethod, Isolated] 
public void TestFakeArgs() 
{ 
    //Arrange 
    Isolate.WhenCalled(() => Environment.GetCommandLineArgs()).WillReturn(new[] { "Your", "Fake", "Args" }); 

    //Act 
    string[] args = Environment.GetCommandLineArgs(); 

    //Assert 
    Assert.AreEqual("Your", args[0]); 
    Assert.AreEqual("Fake", args[0]); 
    Assert.AreEqual("Args", args[0]); 
} 

惩戒Environment.GetCommandLineArgs()只用了一行:

Isolate.WhenCalled(() => Environment.GetCommandLineArgs()).WillReturn(new[] { "Your", "Fake", "Args" }); 

而你并不需要创建新的接口和改变生产代码。

希望它有帮助!

+0

“模拟一切:15天试用” –

+0

不错)但是你知道,有些东西值得购买。它为我节省了大量的金钱和时间 – Eva

+0

我并没有试图暗示它不一定值得为某些人购买(尽管如果你的MSDN支持Fakes,我相信它可以完成同样的事情 - 至少该功能)。对于这个问题的未来读者来说,更多的是不需要以任何理由来投资于工具许可的手段,因此他们不希望浪费时间点击链接。为了未来读者的利益,将这些信息纳入您的答案也许值得。 –