Unit testing a method which relies on multiple methods - c#

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.

Related

How to map any csv file to object with 1 method

I'm trying to map CSV file into class object with C#. My problem is that i have 3 different files, but I want to fallow DRY principles. Can someone tell me how to change 'ParseLine' method to make it possible?
C# consol app.
This is how my FileReader looks like:
public class FileReader<T> : IFileReader<T> where T : Entity
{
private readonly ITransactionReader<T> _transactionReader;
public FileReader(ITransactionReader<T> transactionReader)
{
_transactionReader = transactionReader;
}
public List<T> GetInfoFromFile(string filePath)
{
var lines = File.ReadAllLines(filePath);
var genericLines = new List<T>();
foreach (var line in lines)
{
genericLines.Add(_transactionReader.ParseLine(line));
}
return genericLines;
}
}
public interface IFileReader<T> where T : Entity
{
List<T> GetInfoFromFile(string filePath);
}
This is how the object should look like.
public class TransactionReader : ITransactionReader<Transaction>
{
public Transaction ParseLine(string line)
{
var fields = line.Split(";");
var transaction = new Transaction()
{
Id = fields[0],
Month = int.Parse(fields[1]),
Day = int.Parse(fields[2]),
Year = int.Parse(fields[3]),
IncomeSpecification = fields[4],
TransactionAmount = int.Parse(fields[5])
};
return transaction;
}
}
public interface ITransactionReader<T>
{
T ParseLine(string line);
}
This is how I run it for test purposes.
class Program
{
private static readonly string filePath = "C:/Users/<my_name>/Desktop/C# Practice/ERP/ERP/CsvFiles/Transaction.csv";
static void Main(string[] args)
{
ITransactionReader<Transaction> transactionReader = new TransactionReader();
IFileReader<Transaction> fileReader = new FileReader<Transaction>(transactionReader);
List<Transaction> Test()
{
var obj = fileReader.GetInfoFromFile(filePath);
return obj;
}
var list = Test();
}
}
I'm looking to modify that line:
genericLines.Add(_transactionReader.ParseLine(line));
and method arguments to make it open for any CSV fil.
I don't mind to change that composition into something more effective.

Create Moq for Epplus Excel file

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.

How could we mock methods which are having many database operation inside it

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...

Use Rhino Mock to report the function that was called

I have a failing testcase that depends on an external module.
I want to use Rhino Mock to generate a report on called functions.
I created a minimal example that illustrates my problem:
using NUnit.Framework;
using Rhino.Mocks;
using System;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
object liveFastDieYoung = service.HiddenAmongManyCalls();
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch(Exception e)
{
string[] calls = MagicallyGetTheCallsThatWereMadeToTheMock();
foreach(var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private string[] MagicallyGetTheCallsThatWereMadeToTheMock()
{
return new[]
{
"This is where I am lost, I do not know how to get the calls from the repository."
};
}
}
}
I tried to find something online without success.
Do Rhino Mocks record all calls and can I access that list?
Edit:
An attempt to verify Expectations did not work since I am looking for calls I did not expect.
I could build a list of calls using GetArgumentsForCallsMadeOn. I can reflect on the Interface. I started on a method for that but I currently fail to see how I can convert a MethodInfo to an Action<T>.
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
interfaceMethodInfos.Add(property.GetGetMethod());
interfaceMethodInfos.Add(property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
interfaceMethodInfos.Add(method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
Action<Interface> magic = null; //convert methodinfo into action - still missing
var calls = rhinomock.GetArgumentsForCallsMadeOn(magic); //magic is currently null, here be crash
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more){ callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else { callbuilder.Append(parameter.ToString()); }
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
I was able to use reflection to get the output I wanted.
Here is the minimal example where the test fails and the output contains all method calls.
using NUnit.Framework;
using Rhino.Mocks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
string TestCall2(string arg1, int arg2);
string FULLACCESS { get; set; }
string READONLY { get; }
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
service.TestCall2("callA", 1);
service.TestCall2("callB", 1);
object liveFastDieYoung = service.HiddenAmongManyCalls();
service.TestCall2("callA", 2);
service.TestCall2("callB", 2);
var a = service.FULLACCESS;
var b = service.READONLY;
service.FULLACCESS = "some";
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch (Exception e)
{
var calls = GetCallsList(service);
foreach (var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
AddMethodInfoIfValid(interfaceMethodInfos, property.GetGetMethod());
AddMethodInfoIfValid(interfaceMethodInfos, property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
AddMethodInfoIfValid(interfaceMethodInfos, method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
int paramcount = methodinfo.GetParameters().Length;
object[] args = new object[paramcount];
Action<Interface> lambdacall = (i) => methodinfo.Invoke(i, args);
var calls = rhinomock.GetArgumentsForCallsMadeOn(lambdacall);
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more) { callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else {
callbuilder
.Append('(').Append(parameter.GetType().Name).Append(")'")
.Append(parameter.ToString()).Append("'");
}
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
private static void AddMethodInfoIfValid(List<MethodInfo> interfaceMethodInfos, MethodInfo methodinfo)
{
if (null != methodinfo)
{
interfaceMethodInfos.Add(methodinfo);
}
}
}
}

How to write extension methods for anonymous types?

I'm trying to create a CSV extension method for my enumerable list and I'm stumped. Here's how I created my simple enumerated list:
var CAquery = from temp in CAtemp
join casect in CAdb.sectors
on temp.sector_code equals casect.sector_code
select new
{
CUSIP = temp.equity_cusip,
CompName = temp.company_name,
Exchange = temp.primary_exchange
};
CAquery.WriteToCSVFile();
This is what I have done so far in creating an extension method (which I think is wrong):
public static class CSVExtensions
{
public static void WriteToCSVFile(this IEnumerable<T> myList)
{
Do you see what I'm doing wrong?
You have to specify the generic type parameter in the method signature:
public static class CSVExtensions
{
public static void WriteToCSVFile<T>(this IEnumerable<T> myList)
{
//your code here
}
}
Are you truly trying to write an extension method that should work on any IEnumerable<T> or is your type more specific? If the later is the case you should replace T with the type you want to support (or add sufficient constraints).
Edit:
In light of comments - you should project to a class instead of an anonymous type in your query - then you can use an extension method for this particular type, i.e.:
class CompanyTicker
{
public string CUSIP {get;set;}
public string CompName {get;set;}
public string Exchange {get;set;}
}
Now your query can be:
var CAquery = from temp in CAtemp
join casect in CAdb.sectors
on temp.sector_code equals casect.sector_code
select new CompanyTicker
{
CUSIP = temp.equity_cusip,
CompName = temp.company_name,
Exchange = temp.primary_exchange
};
And your extension method (which now doesn't need to be generic) becomes:
public static class CSVExtensions
{
public static void WriteToCSVFile(this IEnumerable<CompanyTicker> myList)
{
//your code here
}
}
It is possible to do what you are trying to do using reflection. The performance is going to be somewhat worse than if you write non-generic code however.
Here is a complete code sample:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var seq =
Enumerable.Range(0, 100)
.Select(i => new { Name = "Item" + i, Value = i })
;
seq.WriteCsv(Console.Out);
Console.ReadLine();
}
}
public static class CsvExtension
{
public static void WriteCsv<T>(this IEnumerable<T> seq, TextWriter writer)
{
var type = typeof(T);
MethodInfo[] getters = type.GetProperties().Select(pi => pi.GetGetMethod()).ToArray();
// only supporting simple properties
// indexer properties will probably fail
var args = new object[0];
foreach (var item in seq)
{
for (int i = 0; i < getters.Length; i++)
{
if (i != 0)
writer.Write(",");
Object value = getters[i].Invoke(item, args);
var str = value.ToString();
if (str.Contains(",") || str.Contains("\""))
{
var escaped = str.Replace("\"", "\\\"");
writer.Write("\"");
writer.Write(escaped);
writer.Write("\"");
}
else
{
writer.Write(str);
}
}
writer.WriteLine();
}
}
}

Categories