摆脱单元测试对 StreamReader/FileStream 的依赖的好方法是什么?

发布于 2024-10-16 23:15:04 字数 220 浏览 1 评论 0原文

场景如下:

我有一个通过 .NET 中的 FileStream 和 StreamReader 读取文件的方法。我想对这个方法进行单元测试,并以某种方式删除对 StreamReader 对象的依赖。

理想情况下,我希望能够提供自己的测试数据字符串,而不是使用真实的文件。现在,该方法始终使用 StreamReader.ReadLine 方法。为了使这个测试成为可能,修改我现在的设计的方法是什么?

Here's the scenario:

I have a method that reads in a file via a FileStream and a StreamReader in .NET. I would like to unit test this method and somehow remove the dependency on the StreamReader object.

Ideally I would like to be able to supply my own string of test data instead of using a real file. Right now the method makes use of the StreamReader.ReadLine method throughout. What is an approach to modifying the design I have now in order to make this test possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

鯉魚旗 2024-10-23 23:15:04

而是依赖于 StreamTextReader。然后您的单元测试可以使用 MemoryStreamStringReader。 (或者如有必要,从测试程序集中加载资源。)

请注意ReadLine 最初是由 TextReader 声明的,不是 StreamReader 声明的。

Depend on Stream and TextReader instead. Then your unit tests can use MemoryStream and StringReader. (Or load resources from inside your test assembly if necessary.)

Note how ReadLine is originally declared by TextReader, not StreamReader.

心头的小情儿 2024-10-23 23:15:04

最简单的解决方案是让该方法接受 Stream 作为参数,而不是打开自己的 FileStream。您的实际代码可以像往常一样传入 FileStream,而您的测试方法可以使用不同的 FileStream 来测试数据,或者使用您想要测试的内容填充 MemoryStream(不需要文件)。

The simplest solution would be to have the method accept a Stream as a parameter instead of opening its own FileStream. Your actual code could pass in the FileStream as usual, while your test method could either use a different FileStream for test data, or a MemoryStream filled up with what you wanted to test (that wouldn't require a file).

简单 2024-10-23 23:15:04

我突然想到,这是一个很好的机会来研究依赖注入的优点

您可能需要考虑重新设计您的方法,以便它采用返回文件内容的委托。一个委托(生产委托)可能使用 System.IO 中的类,而第二个委托(用于单元测试)则直接以字符串形式返回内容。

Off the top of my head, I'd say this is a great opportunity to investigate the merits of Dependency Injection.

You might want to consider redesigning your method so that it takes a delegate that returns the file's contents. One delegate (the production one) might use the classes in System.IO, while the second one (for unit testing), returns the contents directly as a string.

审判长 2024-10-23 23:15:04

我认为这个想法是依赖注入 TextReader 并模拟它进行单元测试。我认为你只能模拟 TextReader 因为它是一个抽象类。

public class FileParser
{
    private readonly TextReader _textReader;

    public FileParser(TextReader reader)
    {
        _textReader = reader;
    }

    public List<TradeInfo> ProcessFile()
    {
        var rows = _textReader.ReadLine().Split(new[] { ',' }).Take(4);
        return FeedMapper(rows.ToList());
    }

    private List<TradeInfo> FeedMapper(List<String> rows)
    {
        var row = rows.Take(4).ToList();
        var trades = new List<TradeInfo>();
        trades.Add(new TradeInfo { TradeId = row[0], FutureValue = Convert.ToInt32(row[1]), NotionalValue = Convert.ToInt32(row[3]), PresentValue = Convert.ToInt32(row[2]) });
        return trades;
    } 
}

然后使用Rhino Mock进行Mock,

public class UnitTest1
{
    [Test]
    public void Test_Extract_First_Row_Mocked()
    {            
        //Arrange
        List<TradeInfo> listExpected = new List<TradeInfo>();
        var tradeInfo = new TradeInfo() { TradeId = "0453", FutureValue = 2000000, PresentValue = 3000000, NotionalValue = 400000 };
        listExpected.Add(tradeInfo);
        var textReader = MockRepository.GenerateMock<TextReader>();
        textReader.Expect(tr => tr.ReadLine()).Return("0453, 2000000, 3000000, 400000");
        var fileParser = new FileParser(textReader);
        var list = fileParser.ProcessFile();           
        listExpected.ShouldAllBeEquivalentTo(list);         

    }
}

但问题在于从客户端代码传递这样的对象是否是一个好的做法,而不是我觉得它应该在负责处理的类中使用来管理。我同意使用 sep 委托来执行实际代码,并使用 sep 委托来进行单元测试,但这又是生产中的一些额外代码。我可能对依赖注入和模拟甚至文件 IO 打开/读取的想法有点迷失,这实际上不是单元测试的候选者,但文件处理逻辑可以通过仅传递文件的字符串内容来测试( AAA23^YKL890^300000^TTRFGYUBARC)。

任何想法请!谢谢

I think the idea is to dependency inject the TextReader and mock it for unit testing. I think you can only mock the TextReader because it is an abstract class.

public class FileParser
{
    private readonly TextReader _textReader;

    public FileParser(TextReader reader)
    {
        _textReader = reader;
    }

    public List<TradeInfo> ProcessFile()
    {
        var rows = _textReader.ReadLine().Split(new[] { ',' }).Take(4);
        return FeedMapper(rows.ToList());
    }

    private List<TradeInfo> FeedMapper(List<String> rows)
    {
        var row = rows.Take(4).ToList();
        var trades = new List<TradeInfo>();
        trades.Add(new TradeInfo { TradeId = row[0], FutureValue = Convert.ToInt32(row[1]), NotionalValue = Convert.ToInt32(row[3]), PresentValue = Convert.ToInt32(row[2]) });
        return trades;
    } 
}

and then Mock using Rhino Mock

public class UnitTest1
{
    [Test]
    public void Test_Extract_First_Row_Mocked()
    {            
        //Arrange
        List<TradeInfo> listExpected = new List<TradeInfo>();
        var tradeInfo = new TradeInfo() { TradeId = "0453", FutureValue = 2000000, PresentValue = 3000000, NotionalValue = 400000 };
        listExpected.Add(tradeInfo);
        var textReader = MockRepository.GenerateMock<TextReader>();
        textReader.Expect(tr => tr.ReadLine()).Return("0453, 2000000, 3000000, 400000");
        var fileParser = new FileParser(textReader);
        var list = fileParser.ProcessFile();           
        listExpected.ShouldAllBeEquivalentTo(list);         

    }
}

BUT the question lies in the fact whether it is a good practice to pass such an object from the client code rather I feel it should be managed with using within the class responsible for processing. I agree with the idea of using a sep delegate for the actual code and one for the unit testing but again that is a bit of extra code in production. I may be a bit lost with the idea of dependency injection and mocking to even file IO open/read which actually is not a candidate for unit testing but the file processing logic is which can be tested by just passing the string content of the file (AAA23^YKL890^300000^TTRFGYUBARC).

Any ideas please! Thanks

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文