Using mocking with .NET Reflection [closed] - c#

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I am trying to attempt mocking on some reflection (code below). I have been advised to use NSubstitue but I am struggling on how to implement this and to get it started.
At the moment my test stubs are simply like the one below, however on the build server these obviously fail as the DLLs are not present.
[TestMethod]
public void CanGetStudentXml()
{
var student = new ReadStudent();
var results = student.GetStudentXml();
Assert.AreNotEqual(string.Empty, results);
}
Can anyone give me any pointers on how I should go about doing this? Do I need to create mock assemblies? If so, based on the one below, how would I achieve that?
Also is Nsubsitute the best for the job, or would moq be better suited? Which would be the best mocking framework to use?
Sample code:
namespace MokPoc
{
using System.Reflection;
using System;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var students = new ReadStudent();
var results = students.GetStudentXml();
var contacts = students.GetTelephoneXml();
}
}
public enum ReflectedAssembyType
{
SimsProcessesTpPersonStudent,
SimsProcessesTpPersonContact
}
internal class ReflectedAssemblyFactory
{
public static ReflectedAssemblyBase GetReflectedAssembly(ReflectedAssembyType reflectedAssembyType)
{
ReflectedAssemblyBase value = null;
switch (reflectedAssembyType)
{
case ReflectedAssembyType.SimsProcessesTpPersonStudent:
value = new SimsProcessesTpPersonStudent("ThirdPartyProcesses.dll");
break;
case ReflectedAssembyType.SimsProcessesTpPersonContact:
value = new SimsProcessesTpPersonContact("PersonContacts.dll");
break;
}
return value;
}
}
internal abstract class ReflectedAssemblyBase
{
private string path = string.Empty;
private string type = string.Empty;
public string Path
{
get { return this.path; }
set { this.path = value; }
}
public string Type
{
get { return this.type; }
set { this.type = value; }
}
public object InvokeFunction(string name, object[] args)
{
var assemblyToLoad = Assembly.LoadFrom(this.path);
var typeToLoad = assemblyToLoad.GetType(this.type);
var methodToInvoke = typeToLoad.GetMethod(name, args.Select(o => o.GetType()).ToArray());
object obj = Activator.CreateInstance(typeToLoad);
return methodToInvoke.Invoke(obj, args);
}
}
internal sealed class SimsProcessesTpPersonStudent : ReflectedAssemblyBase
{
public SimsProcessesTpPersonStudent(string assembly)
{
this.Path = System.IO.Path.Combine(#"C:\Program Files\Zoosk", assembly);
this.Type = "SIMS.Processes.TPPersonStudent";
}
}
public class ReadStudent
{
public string GetStudentXml()
{
var contacts = ReflectedAssemblyFactory.GetReflectedAssembly(ReflectedAssembyType.SimsProcessesTpPersonStudent);
return (string)contacts.InvokeFunction("GetXmlStudents", new object[] { DateTime.Today });
}
public string GetTelephoneXml()
{
var contacts = ReflectedAssemblyFactory.GetReflectedAssembly(ReflectedAssembyType.SimsProcessesTpPersonContact);
return (string)contacts.InvokeFunction("GetXmlTelephone", new object[] { DateTime.Today });
}
}
}

I have refactored you code to understand what you are trying to test, it seems like you had a lot of classes to do something that seems could be the responsibility of one class, the heart of what you are trying to do is in GetStudentAttributes, I would create a test.dll with a class and public method that returns some strings and then run an actual method to test, in that case you are not using a stub or mock but it is a valid test to ensure your code works. You should also test GetTelephoneXml and GetStudentXML but the only thing you are really testing there is that GetStudentAttributes is inkoved with the appropriate parameters, so when GetStudentXML is called you invokeGetStudentAttributes with "ThirdpartyProcesses.dll" and "GetXmlStudents".
Depending on the framework you use the solution to testing will be different, with Rhynomocks you will have to make the methods virtual to allow the proxy to inherit and invoke your methods, but you can certainly test that the method was called and that the parameters are what you expect, I haven't used nSubstitute, so not sure how to do it there but if the framework is decent you should be able to test those calls and the parameters.
One of the first things that you should do when using driven development is to start by writing the tests first, making sure it fails, making it pass and refactor, usually when you try and retrofit tests to existing code it could get really hard, there are some good resources out there about unit testing, this is a great book about it http://www.amazon.com/Test-Driven-Development-By-Example/dp/0321146530, but in my experience when something is hard to test it usually tells you that your code is too complex or something can be improved, once the code is simplified or fixed testing is usually not a problem.
Good luck and hope this helped a bit!
using System;
using System.IO;
using System.Linq;
using System.Reflection;
namespace MokPoc
{
internal class Program
{
private static void Main(string[] args)
{
var students = new ReadStudentsService();
string results = students.GetStudentXml();
string contacts = students.GetTelephoneXml();
}
}
public class ReadStudentsService
{
private const string ProgramFilesZooskDirectory = #"C:\Program Files\Zoosk";
private const string SimsProcessesTppersonstudent = "SIMS.Processes.TPPersonStudent";
public string GetStudentXml()
{
return GetStudentAttributes("ThirdPartyProcesses.dll", "GetXmlStudents");
}
public string GetTelephoneXml()
{
return GetStudentAttributes("ThirdPartyContacts.dll", "GetXmlTelephone");
}
public string GetStudentAttributes(string dllToUse, string methodToExecute)
{
var fullpath = Path.Combine(ProgramFilesZooskDirectory, dllToUse);
var args = new object[] {DateTime.Today};
var assemblyToLoad = Assembly.LoadFrom(fullpath);
var typeToLoad = assemblyToLoad.GetType(SimsProcessesTppersonstudent);
var methodToInvoke = typeToLoad.GetMethod(methodToExecute, args.Select(o => o.GetType()).ToArray());
var obj = Activator.CreateInstance(typeToLoad);
return (string) methodToInvoke.Invoke(obj, args);
}
}
}

Related

Change files using Roslyn

I'm trying to write a command line tool that modifies some code using Roslyn. Everything seems to go well: the solution is opened, the solution is changed, the Workspace.TryApplyChanges method returns true. However no actual files are changed on disk. What's up? Below is the top level code I'm using.
static void Main(string[] args)
{
var solutionPath = args[0];
UpdateAnnotations(solutionPath).Wait();
}
static async Task<bool> UpdateAnnotations(string solutionPath)
{
using (var workspace = MSBuildWorkspace.Create())
{
var solution = await workspace.OpenSolutionAsync(solutionPath);
var newSolution = await SolutionAttributeUpdater.UpdateAttributes(solution);
var result = workspace.TryApplyChanges(newSolution);
Console.WriteLine(result);
return result;
}
}
I constructed a short program using your code and received the results I expected - the problem appears to reside within the SolutionAttributeUpdater.UpdateAttributes method. I received these results using the following implementation with your base main and UpdateAnnotations-methods:
public class SolutionAttributeUpdater
{
public static async Task<Solution> UpdateAttributes(Solution solution)
{
foreach (var project in solution.Projects)
{
foreach (var document in project.Documents)
{
var syntaxTree = await document.GetSyntaxTreeAsync();
var root = syntaxTree.GetRoot();
var descentants = root.DescendantNodes().Where(curr => curr is AttributeListSyntax).ToList();
if (descentants.Any())
{
var attributeList = SyntaxFactory.AttributeList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("Cookies"), SyntaxFactory.AttributeArgumentList(SyntaxFactory.SeparatedList(new[] { SyntaxFactory.AttributeArgument(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(#"Sample"))
)})))));
root = root.ReplaceNodes(descentants, (node, n2) => attributeList);
solution = solution.WithDocumentSyntaxRoot(document.Id, root);
}
}
}
return solution;
}
}
It was tested using the following class in the sample solution:
public class SampleClass<T>
{
[DataMember("Id")]
public int Property { get; set; }
[DataMember("Id")]
public void DoStuff()
{
DoStuff();
}
}
And it resulted in the following Output:
public class SampleClass<T>
{
[Cookies("Sample")] public int Property { get; set; }
[Cookies("Sample")] public void DoStuff()
{
DoStuff();
}
}
If you take a look at the UpdateAttributes method I had to replace the nodes with ReplaceNodes and updated the solution by calling WithDocumentSyntaxRoot.
I would assume that either one of those two calls is missing or that nothing was changed at all - if you call workspace.TryApplyChanges(solution) you would still receive true as an Output.
Note that using multiple calls of root.ReplaceNode() instead of root.ReplaceNodes() can also result in an error since only the first update is actually used for the modified document - which might lead you to believe that nothing has changed at all, depending on the implementation.

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();
}
}
}

C# error(using interface methods): An object reference is required for the non-static field, method, or property

I'm having trouble using a third party API that has outdated documentation, so I'm trying to figure out why this piece of ##$! isn't working. And by ##$! i mean "code", of course :)
So as far as i know WAPISoap is a public interface that I have obtained by adding a web reference in visual studio.
I also know the Describe() method accepts two parameters, a string and an object of type credential and it returns a string. Any help would be greatly appreciated :)
Here's what i got so far:
using WAPIClient;
using System;
using Project1.WsWWDAPI;
namespace WAPIClient
{
class ResellerAPI
{
public void CallDescribe()
{
String sReturnXml;
Credential m_Crededential = new Project1.WsWWDAPI.Credential();
m_Crededential.Account = "account";
m_Crededential.Password = "password";
String sCLTRID = System.Guid.NewGuid().ToString();
sReturnXml = WAPISoap.Describe(sCLTRID, m_Crededential);
Console.WriteLine(sReturnXml);
}
static void Main(string[] args)
{
ResellerAPI reseller = new ResellerAPI();
reseller.CallDescribe();
}
}
}
The Describe method is not static, which means you need to call it on an instance of the WAPI class:
WsWWDAPI.WAPI m_WAPIObj = null;
WsWWDAPI.Credential m_Crededential = null;
public void Init()
{
m_WAPIObj = new WsWWDAPI.WAPI();
m_Crededential = new WsWWDAPI.Credential();
m_Crededential.Account = "account";
m_Crededential.Password = "password";
}
public void CallDescribe()
{
String sReturnXml;
String sCLTRID = System.Guid.NewGuid().ToString();
sReturnXml = m_WAPIObj.Describe(sCLTRID, m_Crededential);
Console.WriteLine( sReturnXml );
}
static void Main(string[] args)
{
ResellerAPI reseller = new ResellerAPI();
reseller.Init();
reseller.CallDescribe();
}
See: http://products.secureserver.net/guides/wsapiquickstart.pdf
The error is because you use non-static method in static context - you should have instance of the WAPISoap in order to call member function which is not static
It sounds like you need to create an instance of WAPISoap and then call Describe on that instance.

cannot use variable declared class in the function inside the same class. why ?

i have declared a variable in the class 'Main' with name 'context2'. But i cannot use the variable inside the function 'Main_Load'. what am i doing wrong ?
using System;
using System.Windows.Forms;
using Soapi;
using Soapi.Domain;
namespace SO_Console_Client
{
public partial class Main : Form
{
const string apiKey = "*************";
var context2 = new ApiContext(apiKey).Initialize(false);
public Main(String GravatarURL, User user)
{
InitializeComponent();
pictureBox1.Load(GravatarURL); //Loads the Gravatar image from the url
//set the reputation details
lblRep.Text = String.Format("Reputation: {0}", user.Reputation);
//Sets the badge details
lblBadge.Text = String.Format("Badges: gold={0} silver={1} bronze={2}", user.BadgeCounts.Gold, user.BadgeCounts.Silver, user.BadgeCounts.Bronze);
groupBox1.Text = user.DisplayName.ToString();
}
private void Main_Load(object sender, EventArgs e)
{
Soapi.Queries.QuestionsUnansweredQuery query = context2.Official.StackOverflow.Questions.Unanswered;
foreach (Question q in query)
{
try
{
Console.WriteLine(q.Title.ToString());
//Console.WriteLine(q.Body.ToString());
}
catch (System.NullReferenceException ex)
{
}
}
}
}
}
Implicit typing doesn't work with fields; it only works with local variables.
I think this is what your real intention is:
ApiContext context2 = new ApiContext(apiKey);
public Main(String GravatarURL, User user)
{
context2.Initialize(false);
...
}
In the highly unlikely case that ApiContext is some sort of fluent-interface for which ApiContext.Initialize(bool)returns a differentApiContextobject, this should be what you want:
ApiContext context2 = new ApiContext(apiKey).Initialize(false);
although it would be much clearer if you did:
ApiContext context2;
public Main(String GravatarURL, User user)
{
context2 = new ApiContext(apiKey).Initialize(false);
...
}
I really doubt that, though.
This can't compile. var-type variable declarations cannot be at the class level, only the method level.
There are technical issues with allowing var to be used with fields. This is why a concrete type must be specified. Here is an explanation of the issues from Eric Lippert:
Why no var on fields

How to call a generic method through reflection [duplicate]

This question already has answers here:
Select Right Generic Method with Reflection
(13 answers)
Closed 9 years ago.
is it possible to call with reflection a method with "explict type argument" <S> definition
e.g. oObject.Cast<S>() ?
where is:
IList <P> oObject = new List <P>();
I tried with
oObject.getType().InvokeMember( "Cast", BindingFlags.InvokeMethod, null, oObject, null)
but it does not work, does anyone know why?
Here is the complete test code but still it does not work. The last line produce always exception. Is it possible to make it work ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace reflection_tester
{
class CBase
{
public string Ja = "I am the base";
}
class MyClass01 : CBase
{
public string _ID;
public string ID
{
get { return _ID; }
set { _ID = value; }
}
}
class Program
{
public static object wrapper()
{
//return a list of classes MyClass01
IList<MyClass01> lstClass01 = new List<MyClass01>();
MyClass01 oClass01a = new MyClass01();
oClass01a.ID = "1";
MyClass01 oClass01b = new MyClass01();
oClass01b.ID = "2";
lstClass01.Add(oClass01a);
lstClass01.Add(oClass01b);
return lstClass01;
}
static void Main(string[] args)
{
MyClass01 oMy1 = new MyClass01();
oMy1._ID = "1";
MyClass01 oMy2 = new MyClass01();
oMy2._ID = "3";
IList<MyClass01> oListType01 = new List<MyClass01>();
oListType01.Add(oMy1);
oListType01.Add(oMy2);
object oObjectType = new object();
oObjectType = oListType01;
/* this works */
IEnumerable<CBase> enumList = oListType01.Cast<CBase>();
MethodInfo mInfo = typeof(System.Linq.Enumerable).GetMethod("Cast", new[] { typeof(System.Collections.IEnumerable) }).MakeGenericMethod(typeof(CBase));
/* this does not work, why ? throws exception */
IEnumerable<CBase> enumThroughObject = (IEnumerable<CBase>)mInfo.Invoke(oObjectType, null);
return;
}
}
}
The Cast extension method lives on the class Enumerable, and you need to call MakeGenericMethod:
typeof(System.Linq.Enumerable)
.GetMethod("Cast", new []{typeof(System.Collections.IEnumerable)})
.MakeGenericMethod(typeof(S))
.Invoke(null, new object[] { oObjectType })
update: Because the method is static, the first parameter to Invoke should be null
I think you're looking for Type.MakeGenericType

Categories