EntityFramework-Effort icon indicating copy to clipboard operation
EntityFramework-Effort copied to clipboard

EffortException while running unit tests

Open rosieks opened this issue 7 years ago • 13 comments

Hi, I'm using Effort to mock connection to my database connection. It works fine when I run single test, but when I run multiple tests I receive the following exception:

Result StackTrace:  
at Effort.DbConnectionFactory.CreateTransient()
   at Tests.MyTest..ctor() 
----- Inner Stack Trace -----
   at Effort.Provider.EffortProviderConfiguration.RegisterDbConfigurationEventHandler()
   at Effort.Provider.EffortProviderConfiguration.RegisterProvider()
   at Effort.DbConnectionFactory..cctor()
----- Inner Stack Trace -----
   at System.Data.Entity.Infrastructure.DependencyResolution.DbConfigurationManager.AddLoadedHandler(EventHandler`1 handler)
   at System.Data.Entity.DbConfiguration.add_Loaded(EventHandler`1 value)
   at Effort.Provider.EffortProviderConfiguration.RegisterDbConfigurationEventHandler()
Result Message: 
System.TypeInitializationException : The type initializer for 'Effort.DbConnectionFactory' threw an exception.
---- Effort.Exceptions.EffortException : The Effort library failed to register its provider automatically, so manual registration is required.

a) Call the Effort.Provider.EffortProviderConfiguration.RegisterProvider() method at entry point of the application

or

b) Add the following configuration to the App.config file:
   <system.data>
      <DbProviderFactories>
         <add name="Effort.Provider"
              invariant="Effort.Provider"
              description="Effort.Provider"
              type="Effort.Provider.EffortProviderFactory, Effort" />
      </DbProviderFactories>
   </system.data>


   <entityFramework>
      <providers>
         <provider invariantName="Effort.Provider"
                   type="Effort.Provider.EffortProviderServices, Effort" />
      </providers>
   </entityFramework>
-------- System.InvalidOperationException : The Entity Framework was already using a DbConfiguration instance before an attempt was made to add an 'Loaded' event handler. 'Loaded' event handlers can only be added as part of application start up before the Entity Framework is used. See http://go.microsoft.com/fwlink/?LinkId=260883 for more information.

I have added mentioned configuration to app.config but it doesn't work. I can't add RegisterProvides() because I'm using xunit.net and it doesn't provide me any entry point.

Below is my code from MyTest constructor:

var connection = DbConnectionFactory.CreateTransient();
var context = new Mock<PaymentTransactionsDbContext>(connection, true);

rosieks avatar Jul 20 '16 12:07 rosieks

I am having the exact same issue. vs2015, mstest

Terebi42 avatar Jul 26 '16 19:07 Terebi42

Thank you for reporting this. It seems the problem is that the exception is still thrown when the xml configuration is set. As a workaround there should be an app config that disables this exception.

tamasflamich avatar Oct 09 '16 20:10 tamasflamich

Any workarounds? Still experiencing this issue...

darxis avatar Nov 15 '16 18:11 darxis

It happens to me when I use other provider in the same test project and a DbContext with this provider is initialized by test execution process before DbContext with Effort provider.

I have found few workarounds:

  1. Splitting test project into two different assemblies, one for testing with Effort, second for testing with real provider.
  2. Making sure that Effort.Provider.EffortProviderConfiguration.RegisterProvider(); is called before any test method which use DbContext with different provider (ex. [AssemblyInitializeAttribute]).

lukaszgulbinski avatar Jan 24 '17 19:01 lukaszgulbinski

Despite having the provider specified in the app.config file, I was still getting this exception being thrown for my tests that were using Effort. I wasn't using any other provider in that assembly, so I couldn't understand why this exception was still being thrown.

tl;dr version In one of my other tests I was creating a DbModelBuilder which results in the DbConfiguration being accessed and created. Once it's created, adding a handler to the Loaded event (which Effort does) will throw the exception we are seeing here.

Long Version One of my tests creates a DbModelBuilder. This has the following call chain:

DbModelBuilder.ctor()
|-- DbModelBuilder.SelectConventionSet()
    |-- V1ConventionSet.cctor()
        |-- NotMappedTypeAttributeConvention.ctor()
            |-- TypeAttributeConfigurationConvention.ctor()
                |-- DbConfiguration.DependencyResolver
                    |-- InternalConfiguration.Instance
                        |-- InternalConfiguration.GetConfiguration() <-- This creates the configuration!!!

Once the configuration has been created, trying to add a handler to the DbConfiguration.Loaded event will throw an exception. Effort tries to add a handler for that event when it is first used.

I managed to work out which test was causing the configuration to be created by adding my own handler to the DbConfiguration.Loaded event and checking the call stack when that event is raised. The call stack should show you what test caused the event to be raised.

Since adding the event handler can throw an exception, I had to add it to the start of every test. I just created a method and called it at the start of every test.

void TrackLoaded() {
    DbConfiguration.Loaded += delegate(object sender, DbConfigurationLoadedEventArgs e) {
            System.Diagnostics.Debugger.Break();
        };
}

Then I just debugged all my tests (Test->Debug->All Tests). Once I worked out what test was causing the Loaded event to fire, I just added Effort.Provider.EffortProviderConfiguration.RegisterProvider() at the start of that test, and now all of the tests run without a problem!

reduckted avatar Feb 22 '17 23:02 reduckted

@reduckted you're right, i had a small project and i did mock a context and was doing some tests on that scenario, after you pointed this out, i could fix this problem as well, thanks.

millerscout avatar May 22 '17 20:05 millerscout

Is there a way to use EFFORT with an existing DataContextConfiguration? If so how.

JamesIlling avatar May 29 '18 18:05 JamesIlling

@JamesIlling This probably isn't the right location for a question like this, this isn't a general purpose forum. But assuming your context class has the overload exposed which accepts a connection, the following will work

var connection = Effort.DbConnectionFactory.CreateTransient(); var context = new MyContext( connection, null );

Terebi42 avatar May 29 '18 18:05 Terebi42

I have the same issue except it only happens on the build server. The tests run perfectly on my local machine. None of the solutions I have read up on work for me. Any ideas why tests won't run on a second environment?

PhilHardingRiver avatar Apr 18 '19 15:04 PhilHardingRiver

Now that xUnit.NET 2.0 is released, the solution is

  • to implement your own subclass of XunitTestFramework,

  • Do your initialization in the constructor

  • Mark the assembly with a TestFrameworkAttribute.

EG:

using Xunit.Abstractions;
using Xunit.Sdk;

[assembly: Xunit.TestFramework("MyNamespace.MyClassName", "MyAssemblyName")]

namespace MyNamespace
{
    public class MyClassName : XunitTestFramework
    {
        public MyClassName(IMessageSink messageSink) : base(messageSink)
        {
            Effort.Provider.EffortProviderConfiguration.RegisterProvider();
        }
    }
}

GPRogers avatar May 02 '19 14:05 GPRogers

Now that xUnit.NET 2.0 is released, the solution is

to implement your own subclass of XunitTestFramework,

Do your initialization in the constructor

Mark the assembly with a TestFrameworkAttribute.

EG: using Xunit.Abstractions; using Xunit.Sdk;

[assembly: Xunit.TestFramework("MyNamespace.MyClassName", "MyAssemblyName")]

namespace MyNamespace { public class MyClassName : XunitTestFramework { public MyClassName(IMessageSink messageSink) : base(messageSink) { Effort.Provider.EffortProviderConfiguration.RegisterProvider(); } } }

Thanks, a solution to this at last! I was intermittently getting the same error when running unit tests in a TeamCity build, though the tests worked fine in Visual Studio.

dotcom9 avatar Aug 06 '19 16:08 dotcom9

@GPRogers & @dotcom9 What is the solution for NUnit?

StefH avatar Aug 23 '19 06:08 StefH

I was having this issue too in NUnit and this is what I did. I created a class file in the test project.

    [SetUpFixture]
    public class TestSetUpClass
    {
        [OneTimeSetUp]
        public void RunBeforeAnyTests()
        {
            Effort.Provider.EffortProviderConfiguration.RegisterProvider();
        }

        [OneTimeTearDown]
        public void RunAfterAnyTests()
        {
        }
    }

The project will see this file and run it before it starts the tests.

vincentw56 avatar Oct 16 '19 19:10 vincentw56