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
Related
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.
https://msdn.microsoft.com/en-us/data/dn469601.aspx
I am trying to get the strategy mentioned in the linked article implemented in my huge codebase with over 500 entities to improve performance. I am stuck with the following issue.
System.Data.Entity.Core.EntityCommandCompilationException occurred
HResult=0x8013193B Message=An error occurred while preparing the
command definition. See the inner exception for details.
Source= StackTrace:
Inner Exception 1: MappingException: The current model no longer
matches the model used to pre-generate the mapping views, as indicated
by the
ViewsForBaseEntitySets3193163ce55837363333438629c877839ae9e7b7494500b6fd275844cda6d343.MappingHashValue
property. Pre-generated mapping views must be either regenerated using
the current model or removed if mapping views generated at runtime
should be used instead. See
http://go.microsoft.com/fwlink/?LinkId=318050 for more information on
Entity Framework mapping views.
Here's what I have tried. There are a few gaps in the original article on how it needs to be implemented where I might have fallen prey.
Step 1: I have created a class that extends DBMappingViewCache.
public class EFDbMappingViewCache : DbMappingViewCache
{
protected static string _mappingHashValue = String.Empty;
public override string MappingHashValue
{
get
{
return GetCachedHashValue();
}
}
public override DbMappingView GetView(EntitySetBase extent)
{
Dictionary<string, string> dict = GetMappedViewFromCache();
if (extent == null)
{
throw new ArgumentNullException("extent");
}
if(dict.ContainsKey(extent.Name))
{
return new DbMappingView(dict[extent.Name]);
}
return null;
}
public static string GetCachedHashValue()
{
string cachedHash;
string path = HttpContext.Current.Server.MapPath(#"~\EFCache\MappingHashValue.txt");
if (!File.Exists(path))
{
File.Create(path).Dispose();
}
using (var streamReader = new StreamReader(path, Encoding.UTF8))
{
cachedHash = streamReader.ReadToEnd();
}
return cachedHash;
}
public static void UpdateHashInCache(string hashValue)
{
string path = HttpContext.Current.Server.MapPath(#"~\EFCache\MappingHashValue.txt");
using (var streamWriter = new StreamWriter(path, false))
{
streamWriter.Write(hashValue);
}
}
private static void UpdateMappedViewInCache(Dictionary<EntitySetBase, DbMappingView> dict)
{
string path = HttpContext.Current.Server.MapPath(#"~\EFCache\MappingView.json");
Dictionary<String, String> stringDict = new Dictionary<string, string>();
foreach(var entry in dict)
{
stringDict[entry.Key.Name] = entry.Value.EntitySql.ToString();
}
var json = new JavaScriptSerializer().Serialize(stringDict);
using (var streamWriter = new StreamWriter(path, false))
{
streamWriter.Write(json);
}
}
private static Dictionary<String, string> GetMappedViewFromCache()
{
string path = HttpContext.Current.Server.MapPath(#"~\EFCache\MappingView.json");
var json = String.Empty;
using (var streamReader = new StreamReader(path, Encoding.UTF8))
{
json = streamReader.ReadToEnd();
}
Dictionary<String, string> mappedViewDict = new Dictionary<String, string>();
if (!String.IsNullOrEmpty(json))
{
var ser = new System.Web.Script.Serialization.JavaScriptSerializer();
mappedViewDict = ser.Deserialize<Dictionary<String, string>>(json);
}
return mappedViewDict;
}
public static void CheckAndUpdateEFViewCache()
{
using (var ctx = new CascadeTranscationsDbContext(DBHelper.GetConnString()))
{
var objectContext = ((IObjectContextAdapter)ctx).ObjectContext;
var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace
.GetItemCollection(DataSpace.CSSpace);
string computedHashValue = mappingCollection.ComputeMappingHashValue();
string currentHashValue = GetCachedHashValue();
SetHashValue(computedHashValue);
if (computedHashValue != currentHashValue)
{
UpdateHashInCache(computedHashValue);
IList<EdmSchemaError> errors = new List<EdmSchemaError>();
Dictionary<EntitySetBase, DbMappingView> result = mappingCollection.GenerateViews(errors);
UpdateMappedViewInCache(result);
}
}
}
}
I have stored the hashvalue and mapping generated in a file and retrieved it in GetView() method.
I have exposed a public CheckAndUpdateEFViewCache() method which will generate the view mapping when called and store in file.
Step2: Call the CheckAndUpdateEFViewCache() from Global.asax file Application_Start() method.
Step3: Include assembly in the file where context is first called.
[assembly: DbMappingViewCacheType(typeof(Models.Entities.MyDBContext), typeof(EFDbMappingViewCache))]
I am really not sure where this assembly line actually needs to go. There is no information on it in the link. There is a really good chance that Step3 might be where i have gone wrong.
Can someone help with the problem ?
The issue I had faced was because I already had a mapped file generated using EF Tools and it was registered. When the configuration I wrote attempted to register one more time, EF threw an error.
Further I want to add that Cached DB model store improved the performance several folds and I ended up using just that in my project.
Link to Cached DB model store usage
I need to upgrade code calling IronPython from C# and would like to upgrade to IronPython 2.7.5. The problem is that one of the APIs has changed, and I am not familiar enough with the original code to fix it. I have written a console program that exhibits the problem
My Main:
class Program
{
static void Main()
{
var pythonTest = new PythonTest();
pythonTest.LoadScript();
Console.WriteLine("Area = {0}", pythonTest.Evaluate());
}
}
My test class:
public class PythonTest
{
private readonly ScriptEngine _engine;
private readonly ScriptScope _scope;
private ScriptSource _source;
private PythonFunction _currentFunction;
private readonly Dictionary<string, PythonFunction> _functions = new Dictionary<string, PythonFunction>();
private readonly double _scriptInput;
public PythonTest()
{
_scriptInput = 5;
_engine = Python.CreateEngine();
_scope = _engine.CreateScope();
}
public void LoadScript()
{
const string filename = #"../../Scripts/testscript.py";
_source = _engine.CreateScriptSourceFromFile(filename);
_source.Execute(_scope);
string firstFunction = "";
foreach (KeyValuePair<string, object> pair in _scope.GetItems())
{
var pairValue = pair.Value as PythonFunction;
if (pairValue != null)
{
_functions.Add(pair.Key, pairValue);
if (_functions.Count == 1)
{
firstFunction = _functions.Keys.First();
}
}
}
_currentFunction = _functions[firstFunction];
}
public string Evaluate()
{
if (_currentFunction == null)
return null;
var parameters = new ArrayList {_scriptInput};
LanguageContext cxt = Microsoft.Scripting.Hosting.Providers.HostingHelpers.GetLanguageContext(_engine);
var context = new CodeContext(new Scope(), cxt);
object result = _currentFunction.__call__(context, parameters.ToArray());
return result.ToString();
}
}
My test script:
from math import *
def AREA(h):
return (h * h)
This all works with the old Python DLLs. With the new DLLs the instantiation of the CodeContext (in the Evaluate method) is incorrect. The new API uses a PythonDictionary:
public CodeContext(PythonDictionary dict, ModuleContext moduleContext);
I don't know how to modify the code to fix this problem. Any help would be appreciated.
Your LanguageContext is a PythonContext so it can be cast. You can then use that along with a PythonDictionary to create a ModuleContext. Then you can use that along with a PythonDictionary to create your CodeContext:
PythonContext cxt = (PythonContext)Microsoft.Scripting.Hosting.Providers.HostingHelpers.GetLanguageContext(_engine);
PythonDictionary dict = new PythonDictionary();
ModuleContext modctx = new ModuleContext(dict, cxt);
var context = new CodeContext(dict, modctx);
I'm trying to generalize the serialization for one of my projects.
I have three main classes as follows:
test.cs - a simple test object
[Serializable()]
public class test : Attribute {
public string name = "";
public int ID = 0;
public test(string inputName, int inputID) {
name = inputName;
ID = inputID;
}
public test() {}
}
Serialization.cs - my main serialization class
public static void SerializeCollection<T>(string path, List<T> collection, Type type) {
System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(type);
System.IO.StreamWriter file = new System.IO.StreamWriter(path);
writer.Serialize(file, collection);
}
and finally Form1.cs - my form class
private void btnSerialize_Click(object sender, EventArgs e)
{
List<test> test = new List<test>();
test.Add(new test("testing1", 2));
Serialization.SerializeCollection("Test.txt", test, typeof(test));
}
When run and click the button I get this error:
'An unhandled exception of type 'System.InvalidOperationException' occurred in System.Xml.dll
Additional information: There was an error generating the XML document.'
You use incorrect type for serialization, you have change typeof(test) to typeof(List)
private static void SerializationTest()
{
List<test> test = new List<test>();
test.Add(new test("testing1", 2));
SerializeCollection("Test.txt", test, typeof(List<test>));
}
And to be honest, I would avoid type as a parameter for your method in your case:
private static void SerializationTest()
{
const string fileName = "Test.txt";
var tests = new List<test> {new test("testing1", 2)};
SerializeCollection(fileName, tests);
}
public static void SerializeCollection<T>(string fullFileName, IEnumerable<T> items)
{
var writer = new XmlSerializer(items.GetType());
var file = new StreamWriter(fullFileName);
writer.Serialize(file, items);
}
Short question,
Is there a way in .NET 4.0 to take a string that represents the method body, and compile it into a Func/Action, or is there a library to do so?
Clarification:
I need something that will not generate any dll, it needs to be completely dynamic, something like eval() in javascript. I need to convert string into a Func/Action without creating dll.
You can use the CSharpCodeProvider class to compile source code into an assembly.
For example:
var compiler = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
var options = new CompilerParameters { OutputAssembly = path);
var results = compiler.CompileAssemblyFromFile(options, sourceFile);
To compile a single function, you can wrap it in a class with appropriate using statements to create a complete source file, then get a delegate using Reflection:
var assembly = results.CompiledAssembly;
var method = assembly.GetType("WrapperClassName").GetMethod("MethodName");
var delegate = (Action)Delegate.CreateDelegate(typeof(Action), method);
For a more complete example:
static readonly Assembly[] References = new[] { typeof(Enumerable).Assembly, typeof(Component).Assembly };
public Action CompileMethodstring source) {
var options = new CompilerParameters(References.Select(a => a.Location).ToArray()) {
GenerateInMemory = true
};
string fullSource = #"public static class HolderClass { public static void Execute() { \r\n" + source + "\r\n} }";
try {
var compiler = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
var results = compiler.CompileAssemblyFromSource(options, fullSource);
if (results.Errors.Count > 0)
throw new InvalidOperationException(String.Join(
Environment.NewLine,
results.Errors.Cast<CompilerError>().Select(ce => ce.ErrorText)
));
return (Action)Delegate.CreateDelegate(
typeof(Action),
results.CompiledAssembly.GetType("HolderClass").GetMethod("Execute")
);
} finally { options.TempFiles.Delete(); }
}
You could also use CS-Script.Net It is an embedded scripting platform that also you to do the following:
dynamic script = CSScript.LoadCode(#"using System;
public class Script
{
public void SayHello(string greeting)
{
Console.WriteLine(greeting);
}
}")
.CreateObject("*");
script.SayHello("Hello World!");
I've been using in production for almost 2 years now and it has been a great way to create configurable applications. I have a sample project if you are interested.
The CSharpCodeProvider might be what you are looking for. However, you'll need to create valid C# code (meaning, you'll need to create a class for that method).