New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Intermittent System.TypeLoadExceptions and System.Security.VerificationExceptions #1910
Comments
Thanks for making us aware, @maxcherednik. |
Wanted to dive into the Castle Core code base, but then realized that FakeItEasy uses slightly older version of it. Any plans to bump the dependency? Maybe the issue does not exist there anymore. |
Hi, @maxcherednik. The dependency is the lowest version that FakeItEasy will work with. Leaving it as it is allows users to keep using the older Castle.Core if they have a reason to (perhaps other code relies on this version), while giving them the freedom to attempt to upgrade if they want. All it takes is a PackageReference to the newer Castle.Core. If newer Castle.Core fixes the problem, your pain may be relieved by upgrading. I'd give it a try. That being said, I don't know that we've tested with the latest Castle.Core, so you may be buying different pain… |
Okay, I very quickly ran our specs (the main test project) using Castle.Core 5.1.1 on .NET 5.0. 12 tests failed, but it looked like they failed for reasons that would likely not be noticed by a regular user. They were tests that checked our behaviour when certain types couldn't be faked (or couldn't have Dummies made). The creation still fails, but the error message printed is slightly different, due to the Castle.Core changes. I think the gist is that Castle.Core stopped using Whether the issue reported here is fixed is another question. (But the Castle.Core issue you pointed to as the likely root cause does remain open…) |
A bit more info which might give us a lead. I was analysing the test failures and decided to compare the ones which are the same. What I found is that they are not exactly the same.
The difference with the first stack trace is: Question: The interface looks like this: internal interface IQueryProcessor
{
TResult ExecuteLegacyQuery<TResult>(IQuery<TResult> query);
}
internal interface IQuery<TResult>
{
} |
I have another question(lead). I thought dummies are the cheap fakes. So if I have a dependency, but not planning to do anything with it, then I would use In our solution we have both - dummies and fakes of the same interface. In the documentation I can see that dummies are used for simple DTOs. Questions
|
No idea. FakeItEasy does not create (or change) generic parameters called "TEvent", as far as I can tell. Nor does a quick search in GitHub reveal anything like that in the Castle.Core source. |
Depends on what you mean by "cheap". If you're looking at performance, it may be more work to create a Dummy than a Fake. As you can see from How are Dummies made?, we go through 7 steps before possibly creating a Fake to act as a Dummy. And if that doesn't work, we'll do even more work.
This is definitely a use. I'd've phrased it more like
(I'm stealing from the docs). The goal is to signal to the reader of the test "yeah, my API requires me to supply this value, but don't care even a little bit what happens with it in the test. It could be called or not. If called, the return value (if any) or side effect aren't important."
I wouldn't characterize them as simple DTOs, since the goal is definitely not to transfer data. The goal is to not care about any data that a Dummy might represent. If you care in any way about the behaviour of your Dummy, I would not use a Dummy. A Fake, configured or not, may be appropriate, depending on the use case. Or maybe not. It really depends on the dependency and production and test code involved.
You may be using them against the intended spirit. Without seeing examples, I can't say for sure.
No. Or more accurately, based on my current understanding of how those exceptions are being raised (specifically that I have no idea), I don't think there's any way that you could use a Dummy that would increase the likelihood of the exceptions being raised. |
I have added some logging around the failed tests. We are trying to pin point where there problem is. I copied some part of the FakeItEasy code to be able to see the MethodInfo: var interfaceType = typeof(T);
var interfaceMap = proxyType.GetTypeInfo().GetRuntimeInterfaceMap(interfaceType);
interfaceMap.InterfaceMethods
.Zip(interfaceMap.TargetMethods, (interfaceMethod, targetMethod) => new
{
InterfaceMethod = interfaceMethod,
TargetMethod = targetMethod
}).ForEach((obj, k) =>
{
testOutput.WriteLine($"{k}");
testOutput.WriteLine($"Target: {obj.TargetMethod}");
testOutput.WriteLine($"Interface: {obj.InterfaceMethod}");
testOutput.WriteLine(string.Empty);
}); I was trying to find if it is the state of the instance which is broken or if it is the type which is somehow wrong. private void ReportBadThings()
{
_testOutputHelper.WriteLine("Old dummy-------------------------------------------------");
ProxyObjectInspector.Inspect<IQueryProcessor>(_dummyQueryProcessor, _testOutputHelper);
_testOutputHelper.WriteLine("New dummy-------------------------------------------------");
var newDummyQueryProcessor = A.Dummy<IQueryProcessor>();
_testOutputHelper.WriteLine($"Same types: {newDummyQueryProcessor.GetType() == _dummyQueryProcessor.GetType()}");
ProxyObjectInspector.Inspect<IQueryProcessor>(newDummyQueryProcessor, _testOutputHelper);
// if this call succeeds this means that the type is alright and the first instance is somehow broken
var res1 = newDummyQueryProcessor.ExecuteLegacyQuery(new OrderedAssociationsQuery
{
ParentId = Guid.NewGuid()
});
_testOutputHelper.WriteLine($"Result from new dummy: {res1}. Count: {res1.Count}");
var res2 = _dummyQueryProcessor.ExecuteLegacyQuery(new OrderedAssociationsQuery
{
ParentId = Guid.NewGuid()
});
_testOutputHelper.WriteLine($"Result from the broken dummy: {res2}. Count: {res2.Count}");
} What I learned from the output:
Based on this + logs below I can conclude that the issue is the generated type itself. Good run:
Bad run:
Any idea how this generic argument might be different? |
One more bad run. Now the generic type name is not even T, but TEvent.
|
Thanks for continuing on this, @maxcherednik. I wonder, though. The leading thought is that a problem is in or below the Castle.Core project, it may be a better use of your time to follow up exclusively in castleproject/Core#193. Then you don't have to duplicate your efforts, and people can read all the details in one place… (The Castle.Core code is largely over my head, and given work and home commitments, I doubt I'm going to get enough time to educate myself and start contributing in the near future.) |
Just leaving a trace for whoever comes after us :) |
This issue seems to becoming from something in or below Castle.Core, it's unlikely that anything will be done in FakeItEasy to fix it (short of possibly picking up a new Castle.Core if it ends up being fixed there). FakeItEasy maintainers will monitor castleproject/Core#193 and if we need to take action, we will. Until then, closing. |
Version: 7.3.1
Castle.Core version: 4.3.1.
After upgrading our solution from .NET 4.8 to .NET 6 we have seen intermittent (about one failed run in every 6-7 build server test runs) System.TypeLoadExceptions and System.Security.VerificationExceptions with the following messages:
System.Security.VerificationException example:
System.TypeLoadException example:
The common part about all these exceptions is the error about violating the constraint of type parameter and that they are all related to mocks.
I don't believe that this issue directly related to the FakeItEasy, but rather to the underlying library - Castle.Core.
Creating this issue for visibility.
Similar issue with moq4: devlooped/moq#1145
The issue on the Castle.Core side: castleproject/Core#193
The text was updated successfully, but these errors were encountered: