单元测试需要读取文件。我如何使它便携?

问题描述:

我正在与另一位开发人员开发一个开源项目。我正在研究需要读取默认自定义xml配置文件的单元测试。我只能保证回购中的路径部分将在他的机器上。我怎样才能重写这个测试,使其可移植?我不希望测试每次检查代码时都会出现失败。单元测试需要读取文件。我如何使它便携?

public class ConfigurationTests 
{ 
    [TestMethod] 
    public void ConfigurationLoads() 
    { 
     //todo: how do I make this test portable? 
     const string configFile = @"C:\Users\Christopher\Source\Repos\RetailCoderVBE\RetailCoder.VBE\Config\rubberduck.config"; 
     var deserializer = new XmlSerializer(typeof(Rubberduck.Config.Configuration)); 
     using (StreamReader reader = new StreamReader(configFile)) 
     { 
      Rubberduck.Config.Configuration config = (Rubberduck.Config.Configuration)deserializer.Deserialize(reader); 
      Assert.IsNotNull(config); 
     } 
    } 
} 

这是我保证其他开发者将拥有的路径的一部分。在此之前,我需要以某种方式动态获取。

\RetailCoderVBE\RetailCoder.VBE\Config\rubberduck.config 
+0

据我所知,单元测试不应该依赖于系统外部的任何东西 - 正是因为配置不能保证是可移植的。 – Gigi 2014-11-21 13:04:25

+0

这个'TestMethod'应该测试什么?现在,它只测试'XmlSerializer.Deserialize'返回'Rubberduck.Config.Configuration'的实例......但你不是'XmlSerializer'的作者,所以测试它是没有意义的。 – 2014-11-21 13:04:37

+0

对。这就是我问的原因。你如何建议我重写我的测试,以便我不依赖文件?我是单元测试新手,所以我不知道在这里做什么@Gigi。 – RubberDuck 2014-11-21 13:07:47

你应该做的是创建一个接口到一个文件系统,并在测试嘲笑它。访问真实文件系统的这个接口的实现你不能测试,所以它必须尽可能简单。由于XmlSerializer.Deserialize方法接受Stream,因此您的接口可以返回Stream。

public interface IFileAccess 
{ 
    Stream GetFileStream(string fileName); 
} 

public class RealFileAccess : IFileAccess 
{ 
    public Stream GetFileStream(string fileName) 
    { 
      .... 
    } 
} 

public class ConfigLoader 
{ 
    Rubberduck.Config.Configuration LoadConfiguration(IFileAccess fileAccess) 
    { 
      const string configFile = @"C:\Users\Christopher\Source\Repos\RetailCoderVBE\RetailCoder.VBE\Config\rubberduck.config"; 
      var deserializer = new XmlSerializer(typeof(Rubberduck.Config.Configuration)); 
     return (Rubberduck.Config.Configuration)deserializer.Deserialize(fileAccess.GetFileStream(configFile)); 
    } 
} 

然后测试将

[TestMethod] 
public void ConfigurationLoads() 
{ 
    const string configFile = @"C:\Users\Christopher\Source\Repos\RetailCoderVBE\RetailCoder.VBE\Config\rubberduck.config"; 
    IFileAccess fileAccess = Mock.Of<IFileAccess>(); 
    // setup fileAccess 
    Rubberduck.Config.Configuration config = new ConfigLoader().LoadConfiguration(fileAccess); 
    Assert.IsNotNull(config); 
    Mock.Get(fileAccess).Verify(fa => fa.GetFileStream(configFile)); 
} 

然后,您可以甚至是否GetFileStream用正确的参数调用。

+0

非常感谢。这是我需要看到的。我会稍后再尝试。你可能想从你的答案中删除'const string configFile ...'行。这条路线不再需要。 – RubberDuck 2014-11-21 13:28:00

+1

我将它留在那里,以便您可以声明它在GetFileStream调用中使用。我将添加断言。 – 2014-11-21 13:41:42

我的工作需要在一个默认的自定义XML配置文件读取单元测试。

以上强烈表明它不是真正的单元测试。他们(理想上)独立于外部世界。 为什么你需要读取配置文件?什么信息存储在配置中?也许可以使用测试将带有虚拟信息的配置文件添加到项目中,或者使用某个接口将依赖项存储到文件系统。

的标准方法这样做看起来就像

interface IConfigReader{ 
    object GetValue(string key); 
} 

// later in code 
var configDate = _reader.GetValue("ConnectionString");