My first question here. I have looked my query but could not find a helpful answer.
My task is to write unit test case for my excel file. The issue I am facing is that we using Epplus for excel files and I am not sure how can we write unit test cases for this. I looked up and found that we can also use MOQ to mock up. But again I could not find any helpful links for mocking an excel file that uses Epplus. I found this link Unit testing classes that use EPPlus but I am not sure how I can implement this .
I would appreciate if someone can provide a sample of how to write a simple unit test for the excel file. The test can be to check if file uploaded is an excel file or not, checking if the excel is empty or not etc.
Sorry at this moment I dont have any sample. What I can share is the code where I am reading the excel file:
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);
}
}
I had created a helper class as:
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;
}
This is the Test Class i have so far.
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."
}
}
In this case mock IFormFile to return the file stream in your test and pass that to the action under test. Make sure all other necessary dependencies are satisfied.
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
//...
}
}
Now while this should help get you through the current problem, you should really take the advice suggested by other answers about abstracting that tight coupling of ExcelPackage out of the controller into its own concern. Would make unit testing the controller easier in isolation.
You could always do an integration test of the wrapper separately as needed.
A simplified example of an interface abstracted from what is currently in the controller
public interface IExcelService {
Task<JArray> GetDataAsync(Stream stream);
}
which would have an implementation that mirrors the code in the controller
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;
}
}
The controller can now be simplified to follow the Explicit Dependencies Principle
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);
}
}
You would make sure that the interface and implementation are registered with the Dependency Inversion framework in Startup
services.AddScoped<IExcelService, ExcelService>();
So now the controller is only concerned with what it is suppose to do when called at run time. I has no reason to be dealing with implementation concerns
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
}
}
To do an integration test that involves an actual file you can test the service
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.
}
}
Each concern can now be tested on its own.
You don't need to mock EPPlus to test. Your focus should be on testing your code, not EPPlus itself. Just like you wouldn't test any other library you consume. So have your code generate an Excel file in memory using EPPlus and return it. Then in your test use EPPlus to verify your assertions about the file.
Here's an example of a pattern to use:
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();
}
}
The example above is using the Fluent Assertions library, though obviously this isn't necessary.
Related
I want to unit test my technical analysis indicators. I have historical candle data downloaded in JSON format.
What is the correct way to load the JSON data into xUnit and re-use it on multiple methods? All I want is to be able to use the historical data (List<OHLCV>) from all these methods in IndicatorTests.cs.
Is it important to move Data() into a fixture? Since my indicators will be tested in a same file IndicatorTests.cs?
How do I find the JSON files because they are inside the project and not in /bin/Debug. AppDomain.CurrentDomain.BaseDirectory is kinda looking for them in /bin/Debug, which is wrong.
public class IndicatorTests
{
public static IEnumerable<object[]> Data()
{
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Data\TRX_USDT-5m.json");
if (!File.Exists(filePath))
throw new FileNotFoundException($"The historical data file '{filePath}' was not found.");
var data = File.ReadAllText(filePath);
var deserializedData = JsonConvert.DeserializeObject<List<List<decimal>>>(data);
var candles = deserializedData.Select(e => new OHLCV()
{
Timestamp = DateTimeOffset.FromUnixTimeMilliseconds((long)e[0]).DateTime,
Open = e[1],
High = e[2],
Low = e[3],
Close = e[4],
Volume = e[5]
});
foreach (var candle in candles ?? Enumerable.Empty<OHLCV>())
{
yield return new[] { candle };
}
}
[Theory]
[MemberData(nameof(Data))]
public void Test1(List<OHLCV> candle)
{
Console.WriteLine(candle);
}
[Theory]
[MemberData(nameof(Data))]
public void Test2(List<OHLCV> candle)
{
Console.WriteLine(candle);
}
[Theory]
[MemberData(nameof(Data))]
public void Test3(List<OHLCV> candle)
{
Console.WriteLine(candle);
}
}
I would like to make a generic method to import data into my application.
For example, say I have:
private static async Task<int> ImportAccount(string filename)
{
var totalRecords = await GetLineCount(filename);
var ctx = new AccountContext();
var count = 0;
var records = 0;
using (var stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
var data = line.Split('\t');
var acc = new Account(data);
await ctx.Accounts.AddAsync(acc);
// need this to avoid using all the memory
// maybe there is a smarter or beter way to do it
// with 10k it uses about 500mb memory,
// files have million rows+
if (count % 10000 == 1)
{
records += result = await ctx.SaveChangesAsync();
if (result > 0)
{
ctx.Dispose();
ctx = new AccountContext();
}
}
count++;
}
}
}
await ctx.SaveChangesAsync();
ctx.Dispose();
return records;
}
In the above example I am importing data from a tab delimited file into the Accounts db.
Then I have properties, lands, and a whole lot of other db's I need to import.
Instead of having to make a method for each db like the above, I would like to make something like:
internal static readonly Dictionary<string, ??> FilesToImport = new Dictionary<string, ??>
{
{ "fullpath to file", ?? would be what I need to pass to T }
... more files ...
};
private static async Task<int> Import<T>(string filename)
Where T would be the DB in question.
All my classes have 1 thing in common, they all have a constructor that takes a string[] data.
But I have no idea how I could make a method that I would be able to accept:
private static async Task<int> Import<T>(string filename)
And then be able to do a:
var item = new T(data);
await ctx.Set<T>().AddAsync(item);
And if I recall correctly, I would not be able to instantiate T with a parameter.
How could I make this generic Import method and is it possible to achieve?
The easiest way to accomplish this is to pass a generic function that accepts the string line or a string array of split values and returns an object with values set. Use the ctx.AddAsync() method which supports generics and add the entity to the correct set.
private static async Task<int> Import<T>(string filename, Func<string, T> transform) where T : class
{
var totalRecords = await GetLineCount(filename);
var ctx = new AccountContext();
var count = 0;
var records = 0;
using (var stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
var data = line.Split("\t");
var entity = transform(data);
await ctx.AddAsync(entity);
if (count % 10000 == 1)
{
records += result = await ctx.SaveChangesAsync();
if (result > 0)
{
ctx.Dispose();
ctx = new AccountContext();
}
}
count++;
}
}
}
await ctx.SaveChangesAsync();
ctx.Dispose();
return records;
}
// Usage
Import(filename, splits => {
/ * do whatever you need to transform the data */
return new Whatever(splits);
})
Since generic types cannot be constructed by passing a parameter, you will have to use a function as the second type in the dictionary.
Dictionary<string, Func<string, object>> FilesToImport = new Dictionary<string, Func<string, object>>{
{ "fullpath to file", data => new Account(data) },
{ "fullpath to file", data => new Whatever(data) },
{ "fullpath to file", data => new Whatever2(data) },
}
C# have only new() restriction for generic type arguments. But unfortunately it's not possible to force a type to have a constructor with parameters.
One workaround for this is to define an interface like this:
interface IImportedEntity<T>
// where T: YourBaseClass
{
T Init(string[] data);
}
In this case all implementation classes will have to implement such method:
class Account : /*YourBaseClass*/ IImportedEntity<Account>
{
public Account()
{
// for EF
}
// can be made private or protected
public Account(string[] data)
{
// your code
}
// public Account Init(string[] data) => { /*populate current instance*/ return this;};
// can be implemented in base class
public Account Init(string[] data) => new Account(data);
}
Finally you can restrict your generic Import method to deal only with imported entities:
private static async Task<int> Import<T>(string filename)
where T: class, IImportedEntity<T>, new()
{
....
var item = new T();
item = item.Init(data);
await ctx.Set<T>().AddAsync(item);
...
}
Note, if you still want to use this with a dictionary it will be needed to use reflection(example).
I want to write a unit-test in c# to test those method which are having some database operation (2-3 DB operations) and also some other logics written inside it.
private static APIResponse SubmitRequest(HttpWebRequest request, string info)
{
APIResponse responseObj = new APIResponse();
WebResponse response = null;
// save the log into database.
Log.Request(request.Method + " to " + request.RequestUri.ToString() + ": " + info);
try
{
response = request.GetResponse();
}
catch (WebException e)
{
var resp = (HttpWebResponse)e.Response;
if (resp.StatusCode == HttpStatusCode.NotModified)
{
responseObj.StatusCode = HttpStatusCode.NotModified;
responseObj.Headers = resp.Headers;
eTAG = responseObj.Headers["eTag"];
// save the log into the database.
Log.Response("<empty>");
return responseObj;
}
// save the log into the database.
Log.Warning(e.Message);
response = e.Response;
}
if (response == null)
{
Log.Response("<null>");
return null;
}
StreamReader reader = new StreamReader(response.GetResponseStream());
string textResponse = reader.ReadToEnd();
HttpStatusCode status = ((HttpWebResponse)response).StatusCode;
reader.Close();
response.Close();
if (textResponse != null)
{
textResponse = textResponse.Trim();
}
// save the log into the database.
Log.Response(textResponse.Length == 0 ? "<empty>" : textResponse);
if (textResponse.Length == 0)
return null;
responseObj.Headers = response.Headers;
responseObj.Message = textResponse;
responseObj.StatusCode = status;
eTAG = responseObj.Headers["eTag"];
return responseObj;
}
As you can see in snippet , we are saving the logs into the database in between the code various times. How can we mock/stop these logs to be save.
public static void Request(string text)
{
-- code to save the code in db.
}
public static void Response(string text)
{
-- code to save the code in db.
}
How could we achieve ? Any guesses anybody ?
So using Moq, you're test and class you want to test could look something like this.
public class ClassToTest
{
IDataAccessService _dataAccessService;
public ClassTotest(IDataAccessService dataAccessService)
{
_dataAccessService = dataAccessService;
}
public int SomeMethodWeWantToTest()
{
// Do Something.
}
}
Here we use dependency injection to inject the DAL. That way at test time we can pass in a mock.
public class ConcreteDataAccessService : IDataAccessService
{
public List<int> GetSomeNumbersFromTheDatabase()
{
// Call db.
// Get some numbers.
// Return a list of them.
}
}
public IDataAccessService
{
List<int> GetSomeNumbersFromTheDatabase();
}
Here we show an interface which represents our DAL. We have a concrete implementation which implements the interface IDataAccessService This concrete implementation is what we could call at non test runtime.
[TestClass]
public class ClassToTestTests
{
[TestMethod]
public void SomeMethodWeWantToTest_ShouldAddUpAllNumbersFromDatabaseCorrectly()
{
Mock<IDataAccessService> dataAccessServiceMock = new Mock<IDataAccessService>();
dataAccessService.Setup(x=>x.GetSomeNumbersFromTheDatabase()).Returns(new List<int>{1,2,3,4,5});
ClassToTest classToTest = new ClassToTest(dataAccessService.Object());
int expected = 15;
Assert.AreEqual(expected, classToTest.SomeMethodWeWantToTest());
}
}
The test class uses Moq, a mocking framework, to mock the DataAccessService and sets up what we want to return when we call the method GetSomeNumbersFromTheDatabase.
We then instantiate the class we want to test and pass in the mocked DataAccessService to the class constructor.
This way we can test the functionality of ClassToTest.SomeMethodWeWantToTest() without ever hitting an actual database.
Note this code wasn't compiled or checked. It's a rough outline of how to do, very basic, DI and testing. BUt you didn't provide any code in your question...
I have the following code, in a unit test in Visual Studio 2012. I am trying to test a private method the GatherData method is in the ExcelFile class. However When I run the test I am getting a MissingMethodException. How can I invoke a private method in a class so I can unit test?
ExcelFile xFile = new ExcelFile("pathhere");
PrivateObject po = new PrivateObject(xFile);
var retVal = po.Invoke("GatherData");
Here is some of the ExcelFile class:
public class ExcelFile
{
private FileInfo excelFileInfo;
private ExcelWorksheet workSheet;
private Dictionary<string, string> data = new Dictionary<string, string>();
public ExcelFile(string path)
{
this.excelFileInfo = new FileInfo(path);
}
private Dictionary<string, string> GatherData(ExcelWorksheet workSheet)
{
Dictionary<string, string> data = new Dictionary<string, string>();
int endDataRow = workSheet.Dimension.Rows;
for (int rowNumber = 2; rowNumber <= endDataRow; rowNumber++)
{
if (ValidateRow(rowNumber))
{
string columnOneValue = workSheet.Cells[rowNumber, 1].Value.ToString().Trim(),
columnTwoValue = workSheet.Cells[rowNumber, 2].Value.ToString().Trim();
data.Add(columnOneValue, columnTwoValue);
}
}
return data;
}
}
The method GatherData requires an argument of type ExcelWorksheet in order to work. If you create an object of ExcelWorksheet and then use the code:
ExcelFile xFile = new ExcelFile("pathhere");
PrivateObject po = new PrivateObject(xFile);
var retVal = po.Invoke("GatherData", new object[] {excelWorksheetObject});
It should work for you.
Check out this post for more details: Running into System.MissingMethodException: Method Not Found with PrivateObject
Cheers!
Usually you should not test private methods of classes, but only the public interface, then using any sort of reflection for this purpose, in my opinion, is completely the wrong approach, anyway in .NET there is a system to test protected internal methods with InternalsVisibleToAttribute, decorate your under test class with
[assembly:InternalsVisibleTo("YourTestClass")]
so you dont break the encapsulation
I have a MethodA which calls MethodB in a separate class (one which follows an interface).
MethodB had a StreamReader in it, so I refactored the call to new StreamReader() into a new MethodC (in the same class as MethodB).
In order to test MethodA, i need to mock MethodB, but I also need to be able to test MethodB by mocking MethodC.
(I guess it's become clear I'm a little lost.)
namespace JimBob.CsvImporter.Entity
{
public interface IIOManager
{
TextReader ReturnReader(string path);
int GetNumberOfColumnsInFile(string filePath, List<string> errorMessageList);
}
public class IOManager : IIOManager
{
//MethodC
public TextReader ReturnReader(string filePath)
{
return new StreamReader(filePath);
}
//MethodB
public int GetNumberOfColumnsInFile(string filePath, List<String> errorMessageList)
{
int numberOfColumns = 0;
string lineElements = null;
try
{
using (StreamReader columnReader = (StreamReader)ReturnReader(filePath))
{
lineElements = columnReader.ReadLine();
string[] columns = lineElements.Split(',');
numberOfColumns = columns.Length;
}
return numberOfColumns;
}
catch (Exception ex)
{
errorMessageList.Add(ex.Message);
return -1;
}
}
}
public class EntityVerification
{
private IIOManager _iomgr;
public EntityVerification(IIOManager ioManager)
{
this._iomgr = ioManager;
}
//MethodA
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.");
}
At present my test is as follows:
[Test]
public void GetNumberOfColumnsInFile_ReturnsNumberOfColumns_Returns6()
{
Mock<IIOManager> mock = new Mock<IIOManager>();
mock.Setup(x => x.ReturnReader(It.IsAny<string>())).Returns(
new StringReader("the,big,fat,dog,ate,cats"));
EntityVerification testObject = new EntityVerification(mock.Object);
List<String> errorMessageList = new List<string>();
int i = testObject.GetNumberOfColumnsInFile("blabla.txt", errorMessageList);
Assert.AreEqual(i , 6);
}
But this was for when it was part of the Entity Verification Class.
Am i missing something? Any assistance would be appreciated!
In the test for MethodA, Mock MethodB. In a separate test, Mock MethodC to test MethodB.
The test for MethodA is independent from the test for MethodB, so don't over think this too much.