I am currently writing a unit test framework which shall in the end run standard unit tests written in Visual Studio. The Framework is currently not working correctly with accessors. Consider the following test method:
[TestMethod()]
public void TestMethod()
{
ExampleMethods_Accessor target = null;
target = new ExampleMethods_Accessor();
target.SomeMethod();
}
In this example, the accessor has been generated by Visual Studio. The unit test works perfectly fine when run using the Unit Testing environment of Visual Studio. However, I would like to invoke the TestMethod() from within my Framework. At the line "target = new ExampleMethods_Accessor()", the following exception is thrown:
The type initializer for "Proband.ExampleMethods_Accessor" threw an excepition.
Inner exception:
Could not load file or assembly: Proband, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null...
Has anyone an idea of how the Microsoft Unit Testing Framework invokes unit tests? I was thinking it might be due to the missing TestContext object. This is "null" in my case. When starting the unit test in Visual Studio, the TestContext object contains a lot of information. Could it be, that I need to initialize it properly? How would it need to be initialized?
Thanks for all help,
Christian
EDIT:
I kept experimenting with the way accessors are working. I used ILSpy to see what code is being generated into the Proband_Accessor.dll. It turns out that the instruction causing the exception is:
SomeClass_Accessor.m_privateType = new PrivateType("Probant, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Probant.SomeClass");
I modified my unit test code to be like this (just for test):
[TestMethod()]
[DeploymentItem("Proband.dll")]
public void SomeMethodTest()
{
ExampleMethods_Accessor target = null;
ExampleMethods c = null;
try
{
Assembly.Load("Proband, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); // this works fine
PrivateType tx = new PrivateType(typeof(ExampleMethods)); // this works fine as well (also without loading the assembly)
PrivateType t = new PrivateType("Proband, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Proband.ExampleMethods"); // this causes the exception
c = new ExampleMethods(); // this works fine
target = new ExampleMethods_Accessor(); // this causes the exception as well
}
catch (Exception ex)
{
Console.WriteLine();
}
int actual;
actual = target.SomeMethod();
}
I do absolutely not understand, why "new PrivateType("Proband, Version...." does not work. Has anyone an idea?
I have managed to create a workaround for the issue.
To my AppDomain, I am adding an AssemblyResolveEventHandler:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
This event handler contains the following code:
private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
if(args.Name == "Proband, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
{
// resolving correct assembly of type under test
return typeof(ExampleMethods).Assembly;
}
else
{
return null;
}
}
Now the line of code "target = new ExampleMethods_Accessor();" works fine and returns the correct accessor object.
I still do not understand, why the Assembly cannot be resolved automatically.
Even if it is very unlikely that anyone will have the same problem: I hope this answer helps someone :)
I am not doing anything nearly as complex, but I had:
web application project using .NET 3.5
Configuration project using .NET 3.5
Test project using .NET 3.5
I was getting the same BadImageFormat exception when trying to run a unit test using an accessor.
I found the following link:
http://connect.microsoft.com/VisualStudio/feedback/details/677203/even-after-installing-vs2010-sp1-unit-tests-targeting-3-5-framework-fail-if-they-are-using-private-accessor#details
The second work-around solved my problem. I changed the test project to target .NET 4.0 and it worked.
I just had this exact problem, and it was because I removed the DeploymentItem attribute from the test method. Once I added it again, I no longer got the error on the build machine.
[TestMethod]
[DeploymentItem("FedImportServer.dll")] // ** This is necessary for the build machine. **
public void SourceFileStillExistsAfterProcessingFails()
Note: I never got the error when running it locally.
This is the error:
Test method FedImportTests.FedImportServiceHostTest.FileNoLongerExistsAfterSucessfulProcessing threw exception:
System.TypeInitializationException: The type initializer for 'FedImportServer.Processing.FileProcessor_Accessor' threw an exception. ---> System.IO.FileNotFoundException: Could not load file or assembly 'FedImportServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Related
In my c# application (asp.net mvc), I want to use Autofac to handle my dependency injection, but I got an error when atttempting to use the RegisterAssemblyTypes function (error message below).
I did some experiments with a new solution and found that the error seems to be related to a dot in the project name. The test solution looks like this:
AssemblyTest.sln
- Main.csproj
--> Program.cs
- TestLoadAssembly.csproj
--> Test.cs
- TestWith.Dots.csproj
--> Test.cs
The test classes both have one method that prints "Hello" to the screen. Program.cs looks like this:
using System.Reflection;
namespace Main
{
class Program
{
static void Main(string[] args)
{
var x = Assembly.Load(nameof(TestLoadAssembly)); //This works
var y = Assembly.LoadFrom("TestWith.Dots.dll"); // This seems to work
var z = Assembly.Load(nameof(TestWith.Dots)); // Error here
}
}
}
Main is the active project, and there are project references from Main to the other two. All projects are .net 5 projects.
The error I get from line 3:
System.IO.FileNotFoundException: 'Could not load file or assembly 'Dots, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.'
My question is, how do I make the third line (z) work? Since the first line works, but the third line does not, it seems the error is related to the dot in the name, or is there something that I'm missing here? However, as line two shows, it seems to work when loading the dll by referencing it directly by name.
I have been staring myself blind at this, so I would appreciate your help!
I have a C# solution in VS2013, with the Code Contracts extension installed. In my solution I have an application project (ProjectA) and a class library project (ProjectB). ProjectA references ProjectB, and most of ProjectB's public members have contracts associated with them.
I can access most of ProjectB's members from ProjectA without issue, but on one property I am getting a FileNotFoundException, with the message "Could not load file or assembly 'ProjectB.Contracts, Version=....' or one of its dependencies."
Here is what the fusion log has to say:
=== Pre-bind state information ===
LOG: DisplayName = [SolutionName].ProjectB.Contracts, Version=1.0.5953.23121, Culture=neutral, PublicKeyToken=null (Fully-specified)
LOG: Appbase = [SolutionDir]/[SolutionName].ProjectA/bin/Debug/
LOG: Initial PrivatePath = NULL Calling assembly : [SolutionName].ProjectC, Version=1.0.5953.23122, Culture=neutral, PublicKeyToken=null.
xxx
LOG: This bind starts in default load context.
LOG: Using application configuration file: [MyDocuments][SolutionName][SolutionName].ProjectA\bin\Debug[SolutionName].ProjectA.vshost.exe.config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL
[SolutionDir]/[SolutionName].ProjectA/bin/Debug/[SolutionName].ProjectB.Contracts.DLL.
LOG: Attempting download of new URL
[SolutionDir]/[SolutionName].ProjectA/bin/Debug/[SolutionName].ProjectB.Contracts/[SolutionName].ProjectB.Contracts.DLL.
LOG: Attempting download of new URL
[SolutionDir]/[SolutionName].ProjectA/bin/Debug/[SolutionName].ProjectB.Contracts.EXE.
LOG: Attempting download of new URL
[SolutionDir]/[SolutionName].ProjectA/bin/Debug/[SolutionName].ProjectB.Contracts/[SolutionName].ProjectB.Contracts.EXE.
The only references in this solution are standard Framework libraries, other projects in this solution, and one other first-party library (which only references System.dll). I've tried cleaning and rebuilding all projects in this solution, as well as the other first-party library.
I am confused for several reasons.
In the output window, I can see that all of ProjectA and ProjectB's references (and their references) have already been loaded.
Other seemingly similar methods and properties with similar contracts are not causing any issues. Also, I've been using Code Contracts for months in several solutions and I haven't run into this yet.
Shouldn't the contract code already be recompiled into ProjectB.dll? Why does ProjectA even need to know that there is a .Contracts library? (Other than for intellisense and static analysis)
Does anyone know what might be going on here?
Edit: All projects in solution are targeting .NET 4.0 and "Any CPU".
Here are a few things I tried that didn't work:
On the project settings for ProjectA, on the Code Contracts tab, I tried adding ...ProjectB\bin\Debug\CodeContracts to the field Extra Contract Library Paths. This did not change anything.
I also tried manually adding a reference to ProjectB.Contacts.dll. This turned my FileNotFoundException into a MissingMethodException. This made me think the issue was perhaps due to an error by the Code Contracts rewriter. Enter ILSpy.
The contract that was causing the issue was basically this in the original source code:
Contract.Requires<ArgumentNullException>(collection.All(x => x != null));
But I had put it in a contract abbreviator method like this:
public static class PreCondition {
[ContractAbbreviator]
public static void NotIsOrHasNull<T>(IEnumerable<T> collection){
Contract.Requires<ArgumentNullException(collection != null,
"Collection cannot be null.");
Contract.Requires<ArgumentNullException>(collection.All(x => x != null),
"Collection cannot contain null.");
}
}
and called it in a class like this:
public class Class1{
public void DoStuff(IEnumerable<T> collection){
PreCondition.NotIsOrHasNull(collection);
foreach (var x in collection){
//Stuff
}
}
}
When viewing the troublesome method call in ILSpy, I saw it was trying to call an instance method named Class1.NotIsOrHasNull, instead of the static method PreCondition.NotIsOrHasNull.
The Solution:
I changed the PreCondition class to move the LINQ expression into its own Pure function, and change the contract abbreviator to call that method.
public static class PreCondition {
[Pure]
public static Boolean CollectionContainsNull(IEnumerable<T> collection){
Contract.Requires<ArgumentNullException(collection != null);
if (typeof(T).IsValueType)
return false;
return collection.Any(x => Object.Equals(x, null));
}
[ContractAbbreviator]
public static void NotIsOrHasNull<T>(IEnumerable<T> collection){
Contract.Requires<ArgumentNullException(collection != null,
"Collection cannot be null.");
Contract.Requires<ArgumentNullException>(!CollectionContainsNull(collection)),
"Collection cannot contain null.");
}
}
Now the method call in Class1 works and the IL looks correct in ILSpy.
+1 to ILSpy!
A heap of our unit tests are failing under Mono on OS X with the following error:
System.TypeLoadException : Could not load type 'System.Func``2' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
On of the unit tests in question:
[Test]
public void CanAuthenticateValidUser()
{
const string testUsername = "jappleseed";
var repo = new Mock<IUserRepository>();
repo.Setup(x => x.GetByUsername(testUsername)).Returns(GetTestUser());
var authenticator = new Authenticator(repo.Object);
var result = authenticator.Authenticate(testUsername, "test");
Assert.That(result, Is.True);
}
Running against Mono 2.8, with MonoDevelop 2.4.
Anyone got any suggestions to get around this?
Edit:
Should point out this error is coming from the inbuilt "Run Tests" command in the "Unit Tests" pad in MonoDevelop.
Edit 2:
Forcing the runtime as per jpobst suggestion runs in the console. I guess the question has become how does one get MonoDevelop to exhibit run tests under a specific framework?
shimms:Debug shimms$ mono ~/Development/nunit/bin/net-2.0/nunit-console.exe Convergence.Core.Services.Tests.dll
Throws the same exceptions, however:
shimms:Debug shimms$ mono --runtime=v4.0.30319 ~/Development/nunit/bin/net-2.0/nunit-console.exe Convergence.Core.Services.Tests.dll
All tests pass
There isn't an assembly called "mscorelib", it's "mscorlib". Is that a typo? Or a broken reference?
Second try:
Are you sure your tests were compiled against 4.0 (dmcs)?
You can also try overriding the runtime with:
mono --runtime=v4.0.30319 mytests.exe
I'm having a problem with XML deserialization that is baffling me.
I'm building an application that supports local customization of various services that it uses. I've implemented an abstract ServiceLocator class whose methods return various objects. Each custom installation is responsible for implementing a subclass of this and providing implementations of those methods. The meat of this class looks like this:
public abstract class ServiceLocator
{
public static void Initialize(string customFeaturesPath)
{
Assembly a = Assembly.LoadFrom(customFeaturesPath);
Type t = a.GetExportedTypes()
.AsEnumerable()
.Where(x => x.IsSubclassOf(typeof (ServiceLocator)))
.First();
Default = (ServiceLocator)a.CreateInstance(t.FullName);
}
public static ServiceLocator Default { get; private set; }
public abstract DefaultValuesContainer CreateDefaultValuesContainer();
}
This works just fine: I get the path to the custom features assembly from the application configuration file, the program calls Initialize, and then the application can call the various methods on ServiceLocator.Default and they return the appropriate custom implementations of the services.
One of these services is a DefaultValuesContainer. This is a simple object that exposes properties whose values need to be persisted in a user settings file. The idea is that I can serialize this object into a single user setting of type string. It makes for a user setting file that you wouldn't want to edit manually, but I'm cool with that.
Here's a concrete implementation of ServiceLocator.CreateDefaultValuesContainer:
protected override DefaultValuesContainer CreateDefaultValuesContainer(string serializedXml)
{
DefaultValuesContainer c = new ClientDefaultValuesContainer();
if (string.IsNullOrEmpty(serializedXml))
{
return c;
}
XmlSerializer x = new XmlSerializer(c.GetType());
return (DefaultValuesContainer) x.Deserialize(new StringReader(serializedXml));
}
Now here's the thing.
I've built unit tests for this using NUnit. When I run the tests in the test fixture class that exercises the client custom features, they work. When I run the entire test suite, the last line of the above method throws this exception:
System.InvalidOperationException : There is an error in XML document (0, 0).
----> System.IO.FileLoadException : Could not load file or assembly 'ClientCustomFeatures, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Invalid pointer (Exception from HRESULT: 0x80004003 (E_POINTER))
----> System.ArgumentNullException : Value cannot be null.
Parameter name: path1
I'm kind of baffled as to why. The SetUp method still runs, and ServiceLocator.Default still returns an object of type ClientServiceLocator, which means that it has loaded the ClientCustomFeatures assembly. Indeed, the very method that's throwing the exception is in the assembly that I'm being told can't be loaded.
What is the XmlSerializer trying to do here? Why is it trying to load an assembly that's already loaded? What on earth does "Invalid pointer" mean? And above all, how should I be debugging something like this?
If your custom assembly does not know where to load the assembly containing the ClientCustomFeatures class, this will happen. This occurs when you've deployed your custom assembly to a location that is not in the path of your main assembly and your main assembly is not in the gac. So if your custom asseblies are loaded from sub directories of your main assembly this should go away. However, if they are located in arbitrary places, you'll have a problem because they need to load your main assembly as they need access to the ClientCustomFeatures type.
I've had problems with the assembly loader (Fusion?) when one assembly loads another assembly which itself has (non-GAC) references. YourDLL.XmlSerializers.dll might be one such assembly. Try turning off Visual Studio's option to automatically generate an XML serialization assembly (Project options) - this will remove the additional assembly (and hence the dependency on it).
Fusion Log Viewer
To help diagnose assembly loading problems like these, take a look at the Fusion Log Viewer (AKA fuslogvw.exe).
Fusion == the .NET component that locates and loads assemblies.
Try to replace the line:
XmlSerializer x = new XmlSerializer(c.GetType());
with:
XmlSerializer x = new XmlSerializer(c.GetType(), new Type[] { typeof(DefaultValuesContainer), typeof(ClientDefaultValuesContainer) });
I have been trying to get the following code to work(everything is defined in the same assembly) :
namespace SomeApp{
public class A : MarshalByRefObject
{
public byte[] GetSomeData() { // }
}
public class B : MarshalByRefObject
{
private A remoteObj;
public void SetA(A remoteObj)
{
this.remoteObj = remoteObj;
}
}
public class C
{
A someA = new A();
public void Init()
{
AppDomain domain = AppDomain.CreateDomain("ChildDomain");
string currentAssemblyPath = Assembly.GetExecutingAssembly().Location;
B remoteB = domain.domain.CreateInstanceFromAndUnwrap(currentAssemblyPath,"SomeApp.B") as B;
remoteB.SetA(someA); // this throws an ArgumentException "Object type cannot be converted to target type."
}
}
}
What I'm trying to do is pass a reference of an 'A' instance created in the first AppDomain to the child domain and have the child domain execute a method on the first domain. In some point on 'B' code I'm going to call 'remoteObj.GetSomeData()'. This has to be done because the 'byte[]' from 'GetSomeData' method must be 'calculated' on the first appdomain.
What should I do to avoid the exception, or what can I do to achieve the same result?
The actual root cause was your dll was getting loaded from different locations in the two different app domains. This causes .NET to think they are different assemblies which of course means the types are different (even though they have the same class name, namespace etc).
The reason Jeff's test failed when run through a unit test framework is because unit test frameworks generally create AppDomains with ShadowCopy set to "true". But your manually created AppDomain would default to ShadowCopy="false". This would cause the dlls to be loaded from different locations which leads to the nice "Object type cannot be converted to target type." error.
UPDATE: After further testing, it does seem to come down to the ApplicationBase being different between the two AppDomains. If they match, then the above scenario works. If they are different it doesn't (even though I've confirmed that the dll is loaded into both AppDomains from the same directory using windbg) Also, if I turn on ShadowCopy="true" in both of my AppDomains, then it fails with a different message: "System.InvalidCastException: Object must implement IConvertible".
UPDATE2: Further reading leads me to believe it is related to Load Contexts. When you use one of the "From" methods (Assembly.LoadFrom, or appDomain.CreateInstanceFromAndUnwrap), if the assembly is found in one of the normal load paths (the ApplicationBase or one of the probing paths) then is it loaded into the Default Load Context. If the assembly isn't found there, then it is loaded into the Load-From Context. So when both AppDomains have matching ApplicationBase's, then even though we use a "From" method, they are both loaded into their respective AppDomain's Default Load Context. But when the ApplicationBase's are different, then one AppDomain will have the assembly in its Default Load Context while the other has the assembly in it's Load-From Context.
I can duplicate the issue, and it seems to be related to TestDriven.net and/or xUnit.net. If I run C.Init() as a test method, I get the same error message. However, if I run C.Init() from a console application, I do not get the exception.
Are you seeing the same thing, running C.Init() from a unit test?
Edit: I'm also able to duplicate the issue using NUnit and TestDriven.net. I'm also able to duplicate the error using the NUnit runner instead of TestDriven.net. So the problem seems to be related to running this code through a testing framework, though I'm not sure why.
This is a comment to #RussellMcClure but as it is to complex for a comment I post this as an answer:
I am inside an ASP.NET application and turning off shadow-copy (which would also solve the problem) is not really an option, but I found the following solution:
AppDomainSetup adSetup = new AppDomainSetup();
if (AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles == "true")
{
var shadowCopyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (shadowCopyDir.Contains("assembly"))
shadowCopyDir = shadowCopyDir.Substring(0, shadowCopyDir.LastIndexOf("assembly"));
var privatePaths = new List<string>();
foreach (var dll in Directory.GetFiles(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, "*.dll"))
{
var shadowPath = Directory.GetFiles(shadowCopyDir, Path.GetFileName(dll), SearchOption.AllDirectories).FirstOrDefault();
if (!String.IsNullOrWhiteSpace(shadowPath))
privatePaths.Add(Path.GetDirectoryName(shadowPath));
}
adSetup.ApplicationBase = shadowCopyDir;
adSetup.PrivateBinPath = String.Join(";", privatePaths);
}
else
{
adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
adSetup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
}
This will use the shadow-copy directory of the main app-domain as the application-base and add all shadow-copied assemblies to the private path if shadow-copy is enabled.
If someone has a better way of doing this please tell me.