I have this weird problem when trying to mock an interface with MockedClass.Setup(x => x.Method()).
This is the interface I'm mocking.
public interface IClassFactory
{
object GetValueFromFormData(string key, MultipartFormDataStreamProvider provider);
}
This is my test.
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task ClassApiController_ImportClassList_ThrowsNullReferenceExceptionWhenNoClassId()
{
// Arrange
_classFactory = new Mock<IClassFactory>();
// THIS IS THE LINE THAT GIVES AN EXCEPTION
_classFactory.Setup(x => x.GetValueFromFormData("classIdNull", null)).Returns(string.Empty);
ClassApiController controller = new ClassApiController(_classRepository.Object, _surveyRepository.Object, _classFactory.Object);
// Act
string result = await controller.ImportClassList();
}
If you look at my comment "THIS IS THE LINE THAT GIVES AN EXCEPTION" you see I send null, but it doesn't matter if I send the MultipartFormDataStreamProvider as an instansiated class instead, I still get the same exception.
Exception message: System.ArgumentException: Expression of type 'System.Net.Http.MultipartFormDataStreamProvider' cannot be used for parameter of type 'System.Net.Http.MultipartFormDataStreamProvider' of method 'System.Object GetValueFromFormData(System.String, System.Net.Http.MultipartFormDataStreamProvider)'
If you know why I can't mock the method just because it has this object as parameter, please help me, I'm clueless.
Thanks!
EDIT:
See solution in my answer
you should try
_classFactory = Mock.Of<IClassFactory>();
Mock.Get(_classFactory).Setup(x => x.GetValueFromFormData("classIdNull", It.IsAny<MultipartStreamProvider>()))
.Returns(string.Empty);
With the help of #Vignesh.N I finally solved it. The simple answer in this case was that my solution is divided into several projects. Web, Test, Data and so on. In the web project I had referenced the web api .dll:s via Nuget and in the test project I had referenced them directly via Add references -> Assemblies -> Framework. Thereby the .dll:s had identical Versions but not File versions. After making Nuget take care of all the projects web api .dll files it worked instantly.
So all in all, stupid mistake and hard to spot.
Related
I've two WCF services connected to my client. I want to use a User-object, retrieved from service #1, and use this as paramter for service #2. Here is my MVC-Controller TournamentController.cs code:
private readonly GCTournamentServiceClient _tournamentClient = new GCTournamentServiceClient();
public ActionResult Join(int id)
{
GCUserServiceClient userClient = new GCUserServiceClient();
// Get current user
var currentUser = userClient.GetUser(0);
if (currentUser != null)
{
// Get selected tournament
var selectedTournament = _tournamentClient.GetTournament(id);
// Check if there are available seats in the tournament
if (selectedTournament.Seats > selectedTournament.RegistredUsers.Count)
{
// Check if user exist amoung registred users
if (!selectedTournament.RegistredUsers.Contains(currentUser))
{
selectedTournament?.RegistredUsers.Add(currentUser);
}
}
}
}
The error Visual Studio prompt me with:
Argument 1: cannot convert from 'GConnect.Clients.WebClient.GCUserService.User' to 'GConnect.Clients.WebClient.GCTournamentService.User'
So the problem is currentUser, which has the type GCUserService.User. I'm unable to use this as parameter for RegistredUsers
The error itself makes perfect sense, however, I'm not quite sure how I'm suppose to convert this type (properly). Some articles states, that a "shared"-service has to be created, which holds the User-type. I just can't believe, that a solution like that, should be necessary.
I might have misunderstood some basic stuff here (working with WCF and MVC), but please enlighten me, if that's the case.
So the problem is currentUser, which has the type GCUserService.User.
I'm unable to use this as parameter for RegistredUsers
There are 2 approaches to solve this problem:
1)
Create a class library project (Visual Studio) and add the User class in that project, compile it and add its assembly's (.dll) reference to both services and the client (your MVC application). Next retrieve that user object as you are already doing it
var currentUser = userClient.GetUser(0);
GetUser will return the type of User that is defined in a separate assembly which is added as reference as suggested above. The TournamentService will also reference the same assembly and the RegistredUsers.Add(User userToadd) method will take the same User object and WCF runtime should be able to serialise/desterilise it.
2)
In your MVC client application, new up the User object that is acceptable by the TournamentService.RegistredUsers.Add method. Populate its properties from the currentUser and pass in that object as parameter to RegistredUsers.Add method.
Best Practice
Ideally, I would recommend the first approach which is more work but a better practice and that your User class is maintained centrally and code is reused.
Hope this helps!
I am bit curious about one thing which has happen while trying to understand the concept of Service References and Web Service References.
What I did is?
In my project I have added a web service as a Service Reference and trying to get my script run through the use of client.
But while getting result it is throwing an exception as in the following image:
I have tried to trace out the cause but not able to get the proper answer for that.
I have following code for the resultant object.
[
ComVisible(false),
Serializable,
SoapTypeAttribute("RecordList", "http://www.someadd.com/dev/ns/SOF/2.0"),
XmlType(TypeName="RecordList", Namespace="http://www.someadd.com/dev/ns/SOF/2.0")
]
public class MyRecordListWrapper
{
private IxRecordList recordList = null;
private const string XMLW3CSchema = "http://www.w3.org/2001/XMLSchema";
[SoapElement("Headers")]
public Header[] Headers = null;
[SoapElement("Records")]
public Record[] Records = null;
// some methods to work on intialization
public SmRecordListWrapper(ref IxRecordList p_RecordList)
{
recordList = p_RecordList;// record list initialization
Headers = CreateWrapperHeaders(); // will return header class object
Records = CreateWrapperRecords(); // will return record object
}
}
Can anyone tell me why this error is showing for me?
While adding reference as a Web Service Reference
when I add the same reference as a web reference that time the program is not showing any error and runs successfully?
So can anyone tell me what is the difference in working with the same code using service reference and web service reference?
and Which is a correct way to ass references?
Hope I will get some more described answers to make the things easy to understand.
Thanks in advance.
Adding a web reference, visual studio uses xsd.exe to generate the classes from the service metadata. This uses XmlSerializer under the hood.
Adding a service reference, visual studio uses svcutil.exe to generate the classes from the metadata. This uses DataContractSerializer under the hood.
Two separate tools, two outcomes. For general information, DataContractSerializer is a lot less forgiving when it comes to generating classes from metadata.
I am testing code in a MVC HTML helper that throws an error when trying to get the application path:
//appropriate code that uses System.IO.Path to get directory that results in:
string path = "~\\Views\\directory\\subdirectory\\fileName.cshtml";
htmlHelper.Partial(path, model, viewData); //exception thrown here
The exception that is thrown is
System.Web.HttpException: The application relative virtual path '~/Views/directory/subdirectory/fileName.cshtml' cannot be made absolute, because the path to the application is not known.
Following the advice of How to resolve issue with image path when testing HtmlHelper?
I have faked (using Moq):
Request.Url to return a string
Request.RawUrl to return a string
Request.ApplicationPath to return a string
Request.ServerVariables to return a null NameValueCollection
Response.ApplyAppPathModifier(string virtualPath) to return a string
What else is needed to be able to allow this code to run in the context of a unit test run?
Or
What other approach should I be taking to render a Partial view on a dynamically built string?
As an alternative to mocking built-in .net classes, you can
public interface IPathProvider
{
string GetAbsolutePath(string path);
}
public class PathProvider : IPathProvider
{
private readonly HttpServerUtilityBase _server;
public PathProvider(HttpServerUtilityBase server)
{
_server = server;
}
public string GetAbsolutePath(string path)
{
return _server.MapPath(path);
}
}
Use the above class to get absolute paths.
And for For unit testing you can mock and inject an implementation of IPathProvider that would work in the unit testing environment.
--UPDATED CODE
For what it's worth, I ran up against the same error and followed it through the System.Web source to find it occurs because HttpRuntime.AppDomainAppVirtualPathObject is null.
This is an immutable property on the HttpRuntime singleton, initialized as follows:
Thread.GetDomain().GetData(key) as String
where key is ".appVPath". i.e. it comes from the AppDomain. It might be possible to spoof it with:
Thread.GetDomain().SetData(key, myAbsolutePath)
But honestly the approach in the accepted answer sounds much better than mucking around with the AppDomain.
I'm including a solution from a blog post, which is no longer available (http://blog.jardalu.com/2013/4/23/httprequest_mappath_vs_httpserverutility_mappath)
Complete code: http://pastebin.com/ar05Ze7p
Ratna (http://ratnazone.com) code uses "HttpServerUtility.MapPath" for
mapping virtual paths to physical file path. This particular code has
worked very well for the product. In our latest iteration, we are
replacing HttpServerUtility.MapPath with HttpRequest.MapPath.
Under the hoods, HttpServerUtility.MapPath and HttpRequest.MapPath are
the same code and will result in the same mapping. Both of these
methods are problematic when it comes to unit testing.
Search for "server.mappath null reference" in your favourite search
engine. You are going to get over 10,000 hits. Almost all of these
hits are because test code calls HttpContext.Current and
HttpServerUtility.MapPath. When the ASP.NET code is executed without
HTTP, HttpContext.Current will be null.
This issue (HttpContext.Current is null) can be solved very easily by
creating a HttpWorkerRequest and intializing HttpContext.Current with
that. Here is the code to do that -
string appPhysicalDir = #"c:\inetpub\wwwroot";
string appVirtualDir = "/";
SimpleWorkerRequest request = new SimpleWorkerRequest(appVirtualDir, appPhysicalDir, "/", null, new StringWriter());
HttpContext.Current = new HttpContext(request);
With that simple code in unit test, HttpContext.Current is
initialized. Infact, if you notice, HttpContext.Current.Server
(HttpServerUtility) will also be intiailzed. However, the moment, the
code tries to use Server.MapPath, the following exception will get
thrown.
System.ArgumentNullException occurred
HResult=-2147467261
Message=Value cannot be null.
Parameter name: path
Source=mscorlib
ParamName=path
StackTrace:
at System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional)
InnerException:
HttpContext.Current = context;
Infact, if the code uses HttpContext.Current.Request.MapPath, it is
going to get the same exception. If the code uses Request.MapPath, the
issue can be resolved in the unit test easily. The following code in
unit test shows how.
string appPhysicalDir = #"c:\inetpub\wwwroot";
string appVirtualDir = "/";
SimpleWorkerRequest request = new SimpleWorkerRequest(appVirtualDir, appPhysicalDir, "/", null, new StringWriter());
FieldInfo fInfo = request.GetType().GetField("_hasRuntimeInfo", BindingFlags.Instance | BindingFlags.NonPublic);
fInfo.SetValue(request, true);
HttpContext.Current = new HttpContext(request);
In the above code, the request worker will be able to resolve the map
path. This is not enough though, because HttpRequest does not have the
HostingEnvironment set (which resolves MapPath). Unfortunately,
creating a HostingEnvironment is not trivial. So for unit-test, a
"mock host" that just provides the MapPath functionality is created.
Again, this MockHost hacks lot of internal code. Here is the
pseudo-code for the mock host. Complete code can be downloaded here:
http://pastebin.com/ar05Ze7p
public MockHost(physicalDirectory, virtualDirectory){ ... }
public void Setup()
{
Create new HostingEnvironment
Set Call Context , mapping all sub directories as virtual directory
Initialize HttpRuntime's HostingEnvironment with the created one
}
With the above code when MapPath is called on HttpRequest by it should
be able to resolve the path.
As a last step, in the unit test, add the following code -
MockHost host = new MockHost(#"c:\inetpub\wwwroot\", "/");
host.Setup();
Since now a HostingEnvironment has been initialized, the test code
will be able to resolve virtual paths when
HttpContext.Current.Request.MapPath method is called (along with
HostingEnvironment.MapPath and HttpServerUtility.MapPath).
Download MockHost code here: http://pastebin.com/ar05Ze7p
Trying to make parts of ASP.NET happy with various types of tests seems, to me, to be quite fragile. And I am inclined to believe that the mocking route only works if you basically avoid using ASP.NET or MVC and, instead, write your own webserver from scratch.
Instead, just use ApplicationHost.CreateApplicationHost to create a properly-initialized AppDomain. Then run your test code from within that domain using AppDomain.DoCallback.
using System;
using System.Web.Hosting;
public class AppDomainUnveiler : MarshalByRefObject
{
public AppDomain GetAppDomain()
{
return AppDomain.CurrentDomain;
}
}
public class Program
{
public static void Main(string[] args)
{
var appDomain = ((AppDomainUnveiler)ApplicationHost.CreateApplicationHost(
typeof(AppDomainUnveiler),
"/",
Path.GetFullPath("../Path/To/WebAppRoot"))).GetAppDomain();
try
{
appDomain.DoCallback(TestHarness);
}
finally
{
AppDomain.Unload(appDomain);
}
}
static void TestHarness()
{
//…
}
}
Note: when trying this myself, my test runner code was in a separate assembly from the WebAppRoot/bin directory. This is an issue because, when HostApplication.CreateApplicationHost creates a new AppDomain, it sets its base directory to something like your WebAppRoot directory. Therefore, you must define AppDomainUnveiler in an assembly that is discoverable in the WebAppRoot/bin directory (so it must be in your webapp’s codebase and cannot be stored separately in a testing assembly, unfortunately). I suggest that if you want to be able to keep your test code in a separate assembly, you subscribe to AppDomain.AssemblyResolve in AppDomainUnveiler’s constructor. Once your testing assembly gets the AppDomain object, it can use AppDomain.SetData to pass along information about where to load the testing assembly. Then your AssemblyResolve subscriber can use AppDomain.GetData to discover where to load the test assembly from. (I’m not sure, but the sort of objects you can SetData/GetData might be quite limited—I’ve just used strings myself to be safe). This is a bit annoying, but I think it is the best way to separate concerns in this situation.
This happens once you login to the application and you try to add any new url to the http context and trying to create SimpleWorkerRequest.
in my case i have an url to get the documents from remote server and added the url to http context and trying to authenticate the user and create the SimpleWorkerRequest.
var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
var moqRequestContext = new Mock<RequestContext>(MockBehavior.Strict);
request.SetupGet<RequestContext>(r => r.RequestContext).Returns(moqRequestContext.Object);
var routeData = new RouteData();
routeData.Values.Add("key1", "value1");
moqRequestContext.Setup(r => r.RouteData).Returns(routeData);
request.SetupGet(x => x.ApplicationPath).Returns(PathProvider.GetAbsolutePath(""));
public interface IPathProvider
{
string GetAbsolutePath(string path);
}
public class PathProvider : IPathProvider
{
private readonly HttpServerUtilityBase _server;
public PathProvider(HttpServerUtilityBase server)
{
_server = server;
}
public string GetAbsolutePath(string path)
{
return _server.MapPath(path);
}
}
I'm using C#4.0 and i need to unit test a service. The function inside the service returns a path similar to the variable i called expected, this is the path i'm expecting to get back. But when i run this test i'm getting the error that HttpContext.Current is NULL. What can i do to fix this issue so the test can be ran?
[TestMethod]
public void GetPathTest()
{
var expected = System.IO.Path.GetFullPath(HttpContext.Current.Server.MapPath("~/Certificates/"));
var path = _mockService.Setup(o => o.GetPath()).Returns(expected);
}
At the moment I can't find my full wrapper for HttpContext that I used earlier, but at the moment we simply create a context for an empty request and go from there, like this:
SimpleWorkerRequest request = new SimpleWorkerRequest("","","", null, new StringWriter());
HttpContext context = new HttpContext(request);
Then in the unit test initialize or in the unit test itself (before you create expected) you can set the current HttpContext as follows:
HttpContext.Current = context;
Then simply flesh out the fake context and possible fake sessionstate, etc as required.
(Edit: This is all in VS2008, framework 3.5 by the way).
You can try looking at the attributes created for ASP.Net unit testing, like
[HostType("ASP.NET")]
This link to MSDN has quite a good write-up about it
You could decorate your test method with the followings attributes:
[TestMethod]
[HostType("ASP.NET")]
[UrlToTest("http://localhost:xxxx/")]
[AspNetDevelopmentServerHost("$(SolutionDir)\\xxx\\", "/")]
public void TestMethod()
{
...
}
Then adding a Default.aspx file into your unit test proj.
Inside the test method you can easily access to the HttpContext.
If you want to debug, you may use some traces or interrupt the debugging execution with the instruction System.Diagnostics.Debugger.Break()
public void TestMethod()
{
System.Diagnostics.Debugger.Break();
...
}
and then attaching debugger to the process as explained by MSDN:
https://msdn.microsoft.com/en-us/library/vstudio/c6wf8e4z(v=vs.100).aspx
I'm posting this for reference. It's not an easy solution, and talks about Duck Typing (if it quacks..):
http://haacked.com/archive/2007/08/19/why-duck-typing-matters-to-c-developers.aspx
http://haacked.com/archive/2007/09/09/ihttpcontext-and-other-interfaces-for-your-duck-typing-benefit.aspx
It's relevant and worth a read; because there's no IHttpContext it's not possible to create a test environment implementation - until you consider using the Duck Typing library here. Though this is not a direct answer.
Hope that helps.
I have this scenario where a webservice method I'm consuming in C# returns a Business object, when calling the webservice method with the following code I get the exception "Unable to cast object of type ContactInfo to type ContactInfo" in the reference.cs class of the web reference
Code:
ContactInfo contactInfo = new ContactInfo();
Contact contact = new Contact();
contactInfo = contact.Load(this.ContactID.Value);
Any help would be much appreciated.
This is because one of the ContactInfo objects is a web service proxy, and is in a different namespace.
It's a known problem with asmx-style web services. In the past I've implemented automatic shallow-copy to work around it (here's how, although if I were doing it again I'd probably look at AutoMapper instead).
For example, if you have an assembly with the following class:
MyProject.ContactInfo
and you return an instance of it from a web method:
public class DoSomethingService : System.Web.Services.WebService
{
public MyProject.ContactInfo GetContactInfo(int id)
{
// Code here...
}
}
Then when you add the web reference to your client project, you actually get this:
MyClientProject.DoSomethingService.ContactInfo
This means that if, in your client application, you call the web service to get a ContactInfo, you have this situation:
namespace MyClientProject
{
public class MyClientClass
{
public void AskWebServiceForContactInfo()
{
using (var service = new DoSomethingService())
{
MyClientProject.DoSomethingService.ContactInfo contactInfo = service.GetContactInfo(1);
// ERROR: You can't cast this:
MyProject.ContactInfo localContactInfo = contactInfo;
}
}
}
}
It's on that last line that I use my ShallowCopy class:
namespace MyClientProject
{
public class MyClientClass
{
public void AskWebServiceForContactInfo()
{
using (var service = new DoSomethingService())
{
MyClientProject.DoSomethingService.ContactInfo contactInfo = service.GetContactInfo(1);
// We actually get a new object here, of the correct namespace
MyProject.ContactInfo localContactInfo = ShallowCopy.Copy<MyClientProject.DoSomethingService.ContactInfo, MyProject.ContactInfo>(contactInfo);
}
}
}
}
NOTE
This only works because the proxy class and the "real" class have exactly the same properties (one is generated from the other by Visual Studio).
As several of the other answers have suggested, it is because .NET sees them as two different classes. I personally would recommend using something like AutoMapper. I've been using it, and it seems pretty awesome. You can copy your objects in 1-2 lines of code.
Mapper.CreateMap<SourceClass, DestinationClass>();
destinationInstance = Mapper.Map<SourceClass, DestinationClass>(sourceInstance);
Actually this is not a bug. It's a problem with the version changes of your own project!
Because your final run don't use the original imported references on compile!
For example, I was making a chat server, client. I used a packet structure to transmit data on client project.
Then imported the same reference to server project.
When casting Packet packet = (Packet)binaryFormatter.Deserialize(stream); I got the same error. Because the actual running reference at server project is not the reference now at client project! Because I have rebuilt client project many times after!
In casting <new object>=(<new object>) <old object> always the new object needs to be a newer or same version as the old object!
So what I did was I built a separate project to create a DLL for the Packet class and imported the DLL file to both projects.
If I did any change to Packet class, I have to import the reference to both client and server again.
Then the casting won't give the above exception!
How are you referencing the class in your web service project as well as consumer project? If you have simply used a file link, this could well explain the cause of the error. The way serialiasation works for .NET (Web Services or otherwise I believe) is by using reflection to load/dump the data of an object. If the files are simply linked, then they are actually getting compiled to different types in different assemblies, which would explain why you have the same name but can't cast between them. I recommend creating a 'Core' library which both the web service and consumer project references, and contains the ContactInfo class which you use everywhere.
This isn't a problem - it's a feature.
They are two independent classes. Compare the two, and notice that the proxy class has none of the constructors, methods, indexers, or other behavior from the original class. This is exactly the same thing that would happen if you consumed the ASMX service with a Java program.
Seems like you have two different classes on both ends. Your application has ContactInfo class and your webservice also have the ContactInfo class. Both are two completely different classes. One way is to use the WebService class on your side. If you are using ContactInfo inside your web service then it will be serialized and will be available on the client side for use.
You can also modify your References.cs file generated by Visual Studio when the web reference is added. If you remove the proxy generated classes and add a reference (using statements) to your personal classes, you'll be able to use them straight away, without shallow copy / reflection or heavy mapping. (but you'll have to re-apply your modification if you regenerate the proxy layer).
I also tried to serialize the proxy object and deserialize them back in my DTO classes but it was quite heavy resources wise so I ended up modifying the References cs generated layer.
Hope it will help other people coming here :)
Kindly.