Создать Moq для Epplus Excel файл - PullRequest
       24

Создать Moq для Epplus Excel файл

0 голосов
/ 04 декабря 2018

Мой первый вопрос здесь.Я посмотрел свой запрос, но не смог найти полезного ответа.

Моя задача - написать пример модульного теста для моего файла Excel.Проблема, с которой я сталкиваюсь, заключается в том, что мы используем Epplus для файлов Excel, и я не уверен, как мы можем написать для этого тестовые примеры.Я посмотрел вверх и обнаружил, что мы также можем использовать MOQ для макета.Но опять же я не смог найти никаких полезных ссылок для насмешки над файлом Excel, который использует EpplusЯ нашел эту ссылку Классы модульного тестирования, использующие EPPlus , но я не уверен, как мне это реализовать.

Буду признателен, если кто-нибудь предоставит пример того, как написать простой модульный тестдля файла Excel.Тест может состоять в том, чтобы проверить, является ли загруженный файл файлом Excel или нет, проверить, является ли файл Excel пустым или нет и т. Д.

Извините, в данный момент у меня нет образца.То, что я могу поделиться, это код, где я читаю файл Excel:

public class MyController : Controller
{
  [HttpPost("upload")]
  public async Task<IActionResult> UploadFile(IFormFile file)
  {
   JArray data = new JArray();
    using (ExcelPackage package = new ExcelPackage(file.OpenReadStream()))
    {
      ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
      //Check if excel is empty.
      if (worksheet.Dimension == null)
      {
         return BadRequest("File is blank.");
      }
     data = Helper.CreateJson(worksheet);
                }
     return Ok(data);
  }
}

Я создал вспомогательный класс как:

public static JArray CreateJson(ExcelWorksheet worksheet)
{
  JArray data = new JArray();
  JObject jobject = new JObject();

  int rowCount = worksheet.Dimension.End.Row;
  int colCount = worksheet.Dimension.End.Column;

    for (int row = 1; row <= rowCount; row++)
    {
       for (int col = 1; col <= colCount; col++)
        {
          var value = worksheet.Cells[row, col].Value;
          //Excel has 2 columns and I want to create a json from that.
          if (col == 1)              
          {
             jObject.Add("ID", rowValue.ToString());
          }
          else
          {
             jObject.Add("Name", rowValue.ToString());
          }
        }
         data.Add(jObject);
         jObject= new JObject();
     }
   return data;
}

Это тестовый класс, который у меня есть до сих пор.

public class TestClass
{
    private MyController _controller;
    public TestClass()
    {
      _controller = new MyController (); 
    }

      [Fact]
    public void Upload_WhenCalled()
    {
        //var file = new FileInfo(@"C:\myfile.xlsx");
        //...what next?

        var file = new Mock<IFormFile>();
        var content = File.OpenRead(@"C:\myfile.xlsx");

        var result = _controller.UploadFile(file.Object);
        //When I debug it throws error "Object reference not set to an instance of an object."
    }
}

Ответы [ 2 ]

0 голосов
/ 05 декабря 2018

В этом случае макет IFormFile для возврата потока файлов в тесте и передачи его тестируемому действию.Убедитесь, что все другие необходимые зависимости удовлетворены.

public class TestClass {
    private MyController _controller;
    public TestClass() {
      _controller = new MyController (); 
    }

    [Fact]
    public void Upload_WhenCalled() {
        //Arrange
        var content = File.OpenRead(@"C:\myfile.xlsx");
        var file = new Mock<IFormFile>();
        file.Setup(_ => _.OpenReadStream()).Returns(content);

        //Act
        var result = _controller.UploadFile(file.Object);

        //Assert
        //...
    }
}

Теперь, хотя это должно помочь вам справиться с текущей проблемой, вы должны действительно принять совет, предложенный другими ответами об абстрагировании этой тесной связи ExcelPackage изконтроллера в свою собственную заботу.Сделал бы модульное тестирование контроллера проще в изоляции.

При необходимости вы всегда можете выполнить интеграционный тест обертки отдельно.

Упрощенный пример интерфейса, абстрагирующегося от того, что в настоящее время находится в контроллере

public interface IExcelService {
    Task<JArray> GetDataAsync(Stream stream);
}

, которыйбудет иметь реализацию, которая отражает код в контроллере

public class ExcelService: IExcelService {
    public async Task<JArray> GetDataAsync(Stream stream) {
        JArray data = new JArray();
        using (ExcelPackage package = new ExcelPackage(stream)) {
            ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
            if (worksheet.Dimension != null) {
                data = await Task.Run(() => createJson(worksheet));
            }
        }
        return data;
    }

    private JArray createJson(ExcelWorksheet worksheet) {
        JArray data = new JArray();
        int colCount = worksheet.Dimension.End.Column;  //get Column Count
        int rowCount = worksheet.Dimension.End.Row;     //get row count
        for (int row = 1; row <= rowCount; row++) {
            JObject jobject = new JObject();
            for (int col = 1; col <= colCount; col++) {
                var value = worksheet.Cells[row, col].Value;
                //Excel has 2 columns and I want to create a json from that.
                if (col == 1) {
                    jObject.Add("ID", rowValue.ToString());
                } else {
                    jObject.Add("Name", rowValue.ToString());
                }
                data.Add(jObject);
            }
        }
        return data;
    }
}

Теперь контроллер можно упростить, следуя принципу явных зависимостей

public class MyController : Controller {
    private readonly IExcelService excel;
    public MyController(IExcelService excel) {
        this.excel = excel;
    }

    [HttpPost("upload")]
    public async Task<IActionResult> UploadFile(IFormFile file) {
        JArray data = await excel.GetDataAsync(myFile.OpenReadStream());
        if(data.Count == 0)
            return BadRequest("File is blank.");
        return Ok(data);
    }
}

Вы бы сделалиубедитесь, что интерфейс и реализация зарегистрированы в платформе Dependency Inversion в Startup

services.AddScoped<IExcelService, ExcelService>();

Так что теперь контроллер касается только того, что он должен делать при вызове во время выполнения.У меня нет причин заниматься вопросами реализации

public class MyControllerTests {
    [Fact]
    public async Task Upload_WhenCalled() {
        //Arrange
        var content = new MemoryStream();
        var file = new Mock<IFormFile>();
        file.Setup(_ => _.OpenReadStream()).Returns(content);
        var expected = new JArray();
        var service = new Mock<IExcelService>();
        service
            .Setup(_ => _.GetDataAsync(It.IsAny<Stream>()))
            .ReturnsAsync(expected);

        var controller = new MyController(service.Object);

        //Act
        var result = await controller.UploadFile(file.Object);

        //Assert
        service.Verify(_ => _.GetDataAsync(content));
        //...other assertions like if result is OkContentResult...etc
    }
}

Чтобы провести интеграционный тест, включающий в себя реальный файл, вы можете протестировать службу

public class ExcelServiceTests {
    [Fact]
    public async Task GetData_WhenCalled() {
        //Arrange
        var stream = File.OpenRead(@"C:\myfile.xlsx");
        var service = new ExcelService();

        //Act
        var actual = await service.GetDataAsync(stream);

        //Assert
        //...assert the contents of actual data.
    }
}

Теперь можно протестировать каждую задачусвой.

0 голосов
/ 04 декабря 2018

Вам не нужно издеваться над EPPlus для тестирования.Вы должны сосредоточиться на тестировании своего кода, а не на самом EPPlus.Точно так же, как вы не будете тестировать любую другую библиотеку, которую используете.Поэтому пусть ваш код сгенерирует файл Excel в памяти с помощью EPPlus и вернет его.Затем в своем тесте используйте EPPlus для проверки ваших утверждений о файле.

Вот пример использования шаблона:

public class MyReportGenerator : IReportGenerator
{
    /* implementation here */
}

public interface IReportGenerator
{
    byte[] GenerateMyReport(ReportParameters parameters);
}

[TestMethod]
public void TestMyReportGenerate()
{
    //arrange
    var parameters = new ReportParameters(/* some values */);
    var reportGenerator = new MyReportGenerator(/* some dependencies */);

    //act
    byte[] resultFile = reportGenerator.GenerateMyReport(parameters);

    //assert
    using(var stream = new MemoryStream(resultFile))
    using(var package = new ExcelPackage(stream))
    {
        //now test that it generated properly, such as:
        package.Workbook.Worksheets["Sheet1"].Cells["C6"].GetValue<decimal>().Should().Be(3.14m);
        package.Workbook.Worksheets["Sheet1"].Column(5).Hidden.Should().BeTrue();
    }  
}

В приведенном выше примере используется библиотека Fluent Assertions, хотя, очевидно, в этом нет необходимости.

...