3

ユニットテストしたいStreamReaderを使用するメソッドがあります。StreamReaderの作成を別のクラスに分割し、そのクラスをモックしようとしましたが、単体テストでエラーが発生します。

StreamReaderを抽象化するために使用されるクラス/インターフェース

public interface IPathReader
{
    TextReader CreateReader(string path);
}

public class PathReader : IPathReader
{
    public TextReader CreateReader(string filePath)
    {
        return new StreamReader(filePath);
    }
}

GetLastRowInFileを含むクラス(単体テストを試みるメソッドim)。

public interface IIOManager
{
    int GetLastRowInFile(string filePath, List<String> errorMessageList);
}


public class IOManager : IIOManager
{
    private IPathReader _reader;

    public IOManager(IPathReader reader)
    {
        this._reader = reader;
    }

    //...

    public int GetLastRowInFile(string filePath, List<String> errorMessage)
    {
        int numberOfRows = 0;
        string dataRow;

        try
        {
            using (StreamReader rowReader = (StreamReader)_reader.CreateReader(filePath))
            {
                while ((rowReader.Peek()) > -1)
                {
                    dataRow = rowReader.ReadLine();
                    numberOfRows++;
                }
                return numberOfRows;
            }
        }
        catch (Exception ex)
        {
            errorMessage.Add(ex.Message);
            return -1;
        }
    }
}

StreamReaderにはデフォルトのコンストラクターが含まれていないため、直接モックできるとは思わないため、GetLastRowInFileからStreamReaderを作成する必要があります。

質問

  1. CreateReaderの戻りタイプはTextReaderである必要がありますか?
  2. rowReaderに割り当てる前に、返されたTextReaderをStringReaderに明示的にキャストする必要がありますか?
  3. IPathReaderインターフェイスのモックを作成し、代わりにStringReaderを返すようにCreateReaderを設定すると、rowReaderに割り当てられたときに何が起こりますか。同じ継承レベルで何かをキャストすることは不可能だと思いましたか?

継承階層

ユニットテストは次のとおりで、-1を返し続けます

    [Test]
    public void GetLastRowInFile_ReturnsNumberOfRows_Returns3()
    {
        string testString = "first Row" + Environment.NewLine + "second Line" + Environment.NewLine + "Third line";
        List<String> errorMessageList = new List<string>();

        Mock<IPathReader> mock = new Mock<IPathReader>();
        mock.Setup(x => x.CreateReader(It.IsAny<string>())).Returns(new StringReader(testString));

        IOManager testObject = new IOManager(mock.Object);

        int i = testObject.GetLastRowInFile(testString, errorMessageList);              //Replace with It.IsAny<string>()
        Assert.AreEqual(i, 3);
        Assert.AreEqual(errorMessageList.Count, 0);
    }

私は基本的に私が欠けていると仮定しているので、idはこれについていくつかの助けに本当に感謝しています。お時間をいただきありがとうございます。

編集

試験方法:

    public void GetLastRowInFile_ReturnsNumberOfRows_Returns3()
    {
        StubGetLastRowInFile myStub = new StubGetLastRowInFile();
        List<String> errorMessageList = new List<string>();
        IOManager testObject = new IOManager(myStub);
        int i = testObject.GetLastRowInFile(It.IsAny<string>(), errorMessageList);
        Assert.AreEqual(i, 3);
        Assert.AreEqual(errorMessageList.Count, 0);
    }

スタブ宣言:

public class StubGetLastRowInFile : IPathReader
{
    public TextReader CreateReader(string path)
    {
        //string testString = "first Row" + Environment.NewLine + "second Line" + Environment.NewLine + "Third line";
        string testString = "04/01/2010 00:00,1.4314,1.4316";
        UTF8Encoding encoding = new UTF8Encoding();
        UnicodeEncoding uniEncoding = new UnicodeEncoding();

        byte[] testArray = encoding.GetBytes(testString);

        MemoryStream ms = new MemoryStream(testArray);

        StreamReader sr = new StreamReader(ms);

        return sr;
    }
}

編集2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Data;
using System.Globalization;
using System.Collections;
using System.Reflection;
using System.ComponentModel;

namespace FrazerMann.CsvImporter.Entity
{
    public interface IPathReader
    {
        TextReader CreateReader(string path);
    }

    public class PathReader : IPathReader
    {
        public TextReader CreateReader(string filePath)
        {
            return new StreamReader(filePath);
        }
    }


public interface IIOManager
{
    Stream OpenFile(string path);

    int GetLastRowInFile(string filePath, List<String> errorMessageList);

    int GetNumberOfColumnsInFile(string filePath, List<string> errorMessageList);

    bool IsReadOnly(string filePath);
}


public class IOManager : IIOManager
{
    private IPathReader _reader;

    public IOManager(IPathReader reader)
    {
        this._reader = reader;
    }


    public Stream OpenFile(string path)
    {
        return new FileStream(path, FileMode.Open);
    }


    public int GetNumberOfColumnsInFile(string filePath, List<String> errorMessageList)
    {
        int numberOfColumns = 0;
        string lineElements;

        try
        {
            using (StreamReader columnReader = (StreamReader)_reader.CreateReader(filePath))
            {
                lineElements = columnReader.ReadLine();
                string[] columns = lineElements.Split(',');
                numberOfColumns = columns.Length;
            }
        }
        catch (Exception ex)
        {
            errorMessageList.Add(ex.Message);
            numberOfColumns = -1;
        }
        return numberOfColumns;
    }


    public int GetLastRowInFile(string filePath, List<String> errorMessage)
    {
        int numberOfRows = 0;
        string dataRow;

        try
        {
            using (StreamReader rowReader = (StreamReader)_reader.CreateReader(filePath))
            {
                while ((rowReader.Peek()) > -1)
                {
                    dataRow = rowReader.ReadLine();
                    numberOfRows++;
                }
                return numberOfRows;
            }
        }
        catch (Exception ex)
        {
            errorMessage.Add(ex.Message);
            return -1;
        }
    }


    public bool IsReadOnly(string filePath)
    {
        FileInfo fi = new FileInfo(filePath);
        return fi.IsReadOnly;
    }
}


public interface IVerificationManager
{
    void ValidateCorrectExtension(string filePath, List<String> errorMessageList);

    void ValidateAccessToFile(string filePath, List<String> errorMessageList);

    void ValidateNumberOfColumns(string filePath, int dataTypeCount, List<String> errorMessageList);

    int ValidateFinalRow(int finalRow, string filePath, List<String> errorMessageList);

    void ValidateRowInputOrder(int initialRow, int finalRow, List<String> errorMessageList);

    void EnumeratedDataTypes(UserInputEntity inputs, List<String> errorMessageList);

    int GetProgressBarIntervalsForDataVerification(int initialRow, int finalRow, List<String> errorMessageList);
}


public class VerificationManager : IVerificationManager
{
    private IIOManager _iomgr;

    public VerificationManager(IIOManager ioManager)
    {
        this._iomgr = ioManager;
    }

    public void ValidateCorrectExtension(string filePath, List<String> errorMessageList)
    {
        if (filePath.EndsWith(".txt", StringComparison.OrdinalIgnoreCase) | filePath.EndsWith(".csv", StringComparison.OrdinalIgnoreCase)) { }
        else
        {
            errorMessageList.Add("Selected file does not have a compatable extension.");
        }
    }

    public void ValidateAccessToFile(string filePath, List<String> errorMessageList)
    {
        try
        {

            if (_iomgr.IsReadOnly(filePath) == true) { }
            else
            {
                errorMessageList.Add("Can not read/write to the specified file.");
            }
        }
        catch (Exception e)
        {
            errorMessageList.Add(e.Message);
        }
    }

    public void ValidateNumberOfColumns(string filePath, int userSpecifiedColumnCount, List<String> errorMessageList)
    {
        int numberOfColumnsInFile = _iomgr.GetNumberOfColumnsInFile(filePath, errorMessageList);

        if (userSpecifiedColumnCount != numberOfColumnsInFile) errorMessageList.Add("Number of columns specified does not match number present in file.");
    }

//**TEST APPLIES HERE**

    public int ValidateFinalRow(int finalRow, string filePath, List<String> errorMessageList)
    {
        int totalNumberOfRowsInFile = 0;

        totalNumberOfRowsInFile = _iomgr.GetLastRowInFile(filePath, errorMessageList);

        if (totalNumberOfRowsInFile != -1)
        {
            if (finalRow == 0)
            {
                return totalNumberOfRowsInFile;
            }
            else
            {
                if (finalRow > totalNumberOfRowsInFile)
                {
                    errorMessageList.Add("Specified 'Final Row' value is greater than the total number of rows in the file.");
                }
            }
        }
        return 0;
    }

    public void ValidateRowInputOrder(int initialRow, int finalRow, List<String> errorMessageList)
    {
        if (initialRow > finalRow)
        {
            errorMessageList.Add("Initial row is greater than the final row.");
        }
    }

    public void EnumeratedDataTypes(UserInputEntity inputs, List<String> errorMessageList)
    {
        inputs.EnumeratedDataTypes = new int[inputs.DataTypes.Count];
        try
        {
            for (int i = 0; i < inputs.DataTypes.Count; i++)
            {
                inputs.EnumeratedDataTypes[i] = (int)Enum.Parse(typeof(Enumerations.ColumnDataTypes), inputs.DataTypes[i].ToUpper());
            }
        }
        catch (Exception ex)
        {
            errorMessageList.Add(ex.Message);
        }
    }

    public int GetProgressBarIntervalsForDataVerification(int initialRow, int finalRow, List<String> errorMessageList)
    {
        int progressBarUpdateInverval = -1;

        try
        {
            int dif = (finalRow - initialRow) + 1;
            progressBarUpdateInverval = dif / 100;

            if (progressBarUpdateInverval == 0)
            {
                progressBarUpdateInverval = 1;
            }
        }
        catch (Exception ex)
        {
            errorMessageList.Add(ex.Message);
        }
        return progressBarUpdateInverval;
    }
}



public class EntityVerification
{

    private VerificationManager _vmgr;

    public EntityVerification(VerificationManager vManager)
    {
        this._vmgr = vManager;
    }


    public void VerifyUserInputManager(UserInputEntity inputs, List<string> errorMessageList)
    {
        _vmgr.ValidateCorrectExtension(inputs.CsvFilePath ,errorMessageList);
        _vmgr.ValidateCorrectExtension(inputs.ErrorLogFilePath, errorMessageList);

        _vmgr.ValidateAccessToFile(inputs.CsvFilePath, errorMessageList);
        _vmgr.ValidateAccessToFile(inputs.ErrorLogFilePath, errorMessageList);

        _vmgr.ValidateNumberOfColumns(inputs.CsvFilePath, inputs.DataTypes.Count, errorMessageList);

        inputs.FinalRow = _vmgr.ValidateFinalRow(inputs.FinalRow, inputs.CsvFilePath, errorMessageList);

        _vmgr.ValidateRowInputOrder(inputs.InitialRow, inputs.FinalRow, errorMessageList);

        _vmgr.EnumeratedDataTypes(inputs, errorMessageList);

        inputs.ProgressBarUpdateIntervalForDataVerification = _vmgr.GetProgressBarIntervalsForDataVerification(inputs.InitialRow, inputs.FinalRow, errorMessageList);
    }
}
}

テストメソッド(VerificationManagerクラスの最後から3番目のメソッドに適用されます)

    [Test]
    public void ValidateFinalRow_FinalRowReturned_Returns6()
    {
        List<String> errorMessageList = new List<string>();                             //Remove if replaced

        Mock<IIOManager> mock = new Mock<IIOManager>();
        mock.Setup(x => x.GetLastRowInFile(It.IsAny<String>(), errorMessageList)).Returns(6);

        VerificationManager testObject = new VerificationManager(mock.Object);
        int i = testObject.ValidateFinalRow(0, "Random", errorMessageList);             //Replace with It.IsAny<string>()  and It.IsAny<List<string>>()
        Assert.AreEqual(i, 6);
    }
4

1 に答える 1

9

ここでモックを使用する理由は明確ではありません。

はい、TextReader必須の代わりに使用するStreamReaderと、柔軟性が向上します。StreamReaderパラメーターまたは戻り値の型として明示的に指定しているケースはほとんどありません。

のテスト データを提供する場合はStreamReader、単にStreamReaderラッピングを作成します。MemoryStream

を返すとStringReader、それがキャストされたとき(またはモックフレームワーク自体で)、実際に例外が発生します。残念ながら、例外処理が広すぎるため、その問題を確認するのは困難です。あなたのcatchブロックはおそらくキャッチするだけIOExceptionです-実際に何かがあれば。(リソースを読み取ることができない場合、本当に -1 を返しますか? 例外をバブルアップさせないのはなぜですか?) キャッチExceptionは非常にまれであるべきです - 基本的には、グランド操作の最上位レベル (Web サービスなど) でのみです。 request) を使用して、他の操作を続行できる場合にプロセスを強制終了しないようにします。

于 2012-08-09T09:13:28.667 に答える