-1

I'm writing some tests atm for my project. In my test i'm using a service that i inject with dependency injection to the Xunit project.

        private readonly Service _service;

        public ServicesTests(Service service)
        {
            _service = service;
        }

        [Theory]
        [ClassData(typeof(PersonData))]
        public async void PersonService_ShouldCountPersons(Person person, int expected)
        {
            //pseudo
            // Act.
            int actual = await _service.CountPersons(person); //Inside CountPersons is where the method call to the repository takes place.

            // Assert.
            Assert.Equal(expected, actual);
        } 
//My Startup
public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<Service>();
        }
    }

Full exception/error message: Message: System.InvalidOperationException : The ConnectionString property has not been initialized.

Stack Trace: SqlConnection.PermissionDemand() SqlConnectionFactory.PermissionDemand(DbConnection outerConnection) DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) SqlConnection.TryOpen(TaskCompletionSource`1 retry) SqlConnection.OpenAsync(CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- SqlMapper.QueryRowAsync[T](IDbConnection cnn, Row row, Type effectiveType, CommandDefinition command) line 473 Repository.GetPersons() line 64 Service.CountPersons(Person person) line 95 Service.CountPersons(Person person) line 130 ServicesTests.PersonService_ShouldCountPersons(Person person, int expected) line 38

This service then call a method in another project, and inside this method i call a repository method that returns some data from the database (using dapper).

When i try to run the test i get the exception "The ConnectionString property has not been initialized." which i understand, my app is not running.. But i'm struggling with how i should set up my database connection for my Xunit test project. Should i add a app.config or appsettings.json or something to get this to work? Or do i need some code to accomplish this?

I hope you understand my question!

  • 2
    You configure this in the code that created `_service`. You didn't post that code so it's impossible to help – Panagiotis Kanavos Aug 25 '21 at 13:36
  • Have updated, or do you need more info? @PanagiotisKanavos – byteArrayJake Aug 25 '21 at 13:42
  • Service should be an interface so you can mock it. Look into the Moq library for help. – GH DevOps Aug 25 '21 at 13:43
  • 1
    Yes. This doesn't show how the service was created at all. Where does the exception occur? What *is* the exception? The full exception text returned by `Exception.ToString()` shows where the exception was thrown (file and line), and the stack trace with the function calls that led to it – Panagiotis Kanavos Aug 25 '21 at 13:44
  • 1
    @GHDevOps or not. You mock things you *don't* want to test. In some cases you only care about a return value, in others you care whether you can connect to the database, in others whether you can get the expected HTTP response – Panagiotis Kanavos Aug 25 '21 at 13:44
  • I have added a Startup.cs to my Xunit project that adds services.AddSingleton(); so i can inject it to my test class. i guess its here i dont understand my next step to get it up and running. @PanagiotisKanavos – byteArrayJake Aug 25 '21 at 13:49
  • And where does that `Startup` load the configuration settings? – Panagiotis Kanavos Aug 25 '21 at 13:50
  • I guess its not loading any configuration settings at this moment! I'm sorry but i'm super new to writing Xunit tests. @PanagiotisKanavos – byteArrayJake Aug 25 '21 at 13:57
  • In any case, check [Integration tests in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-5.0#client-options) and the other articles in the [Testing](https://learn.microsoft.com/en-us/aspnet/core/test/razor-pages-tests?view=aspnetcore-5.0) section. Instead of connecting to the database you can create a DbContext using either the In-Memory provider or SQLite in in-memory mode, and test whether actions can work. You can use the WebApplicationFactory to get and test the actual HTTP responses – Panagiotis Kanavos Aug 25 '21 at 14:00
  • You can find more about testing EF Core in the [Testing section](https://learn.microsoft.com/en-us/ef/core/testing/). If you actually post your `Startup` people may be able to show you how to use the in-memory providerr – Panagiotis Kanavos Aug 25 '21 at 14:02
  • Oops! Must have stepped on @PanagiotisKanavos toes! My bad... – GH DevOps Aug 25 '21 at 14:04
  • Mocking the raw DbDataConnection used by Dapper is harder though. Again, you *could* use SQLite in in-memory mode, provided you used a `DbConnection` instead of a `SqlConnection` – Panagiotis Kanavos Aug 25 '21 at 14:04
  • Super thanks for your answers Panagiotis, the startup dont have much right now more then i'm adding my service, i should say as well that it's not a test for http requests/controllers, i simply using a service that has this CountPersons method, inside this method i'm getting some data from the database using dapper, then i use the data i get back from the database to do some calculations on it and return it. @PanagiotisKanavos – byteArrayJake Aug 25 '21 at 14:10
  • @byteArrayJake post it anyway. .NET Core's Configuration middleware can work with `Dictionary` objects [as the docs show](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#memory-configuration-provider). You could add some hard-coded values just to get the test to run – Panagiotis Kanavos Aug 25 '21 at 14:13
  • Have posted it, hmm feel like in every example they are using Entity Framework to talk to the database. I'm using Dapper, can this be the problem? @PanagiotisKanavos – byteArrayJake Aug 26 '21 at 09:04
  • No. You still haven't posted the code that retrieves the connection string and creates the connection, so you still force people to guess. Perhaps you inject `IConfiguration` and use it to retrieve the connection string? And don't check whether the return value is correct? Perhaps you use the obsolete `ConfigurationManager` that reads from `app.config`? All we know is that at some point you pass an empty string to a `SqlConnection` constructor. You didn't even post the full exception text that would show *where* you do that – Panagiotis Kanavos Aug 26 '21 at 09:09
  • I will try to explain, I'm having an Azure functions project that has their own Startup class and local.settings.json file where from the service that calls the repository class gets the connection string from, via Environment.GetEnvironmentVariable("SqlConnectionString"). Now I have set up my unit test project in the same solution, added the test I'm showing and startup class, And I have injected the service where I know I have a method I wanna test, and in that method it calls a repository method. @PanagiotisKanavos – byteArrayJake Aug 27 '21 at 12:48
  • In that case you'd need to set the environment variable on your test machine as well, otherwise the connection string will be empty. A better solution would be to [configure .NET Configuration to use environment variables](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#environment-variables). This way you could store the connection string anywhere you wanted (env variables, settings file, dictionary). – Panagiotis Kanavos Aug 27 '21 at 12:59
  • Ok, will take a look. I have posted the full error message. @PanagiotisKanavos – byteArrayJake Aug 27 '21 at 13:38

2 Answers2

0

First of all if you are writing unit tests, you are going too deep. You should test the unit, not entire application/flow, you get the idea.. There are various Mocking frameworks that you can use to "mock"/immitate dependencies' behavior. Most common & powerful mocking framework is Moq for .NET stack.

In case you are writing integration tests and want to test actual behavior of the application, I believe you will have to provide an interface to be able to pass the configuration for the test purposes.

David Oganov
  • 976
  • 1
  • 11
  • 23
  • Even as a comment this is wrong. There are various types of testing but all of them can use XUnit. And even a unit test may want to make sure the controller works properly, without mocks that actually hide problems. Sure, a controller action may work with a dummy request, and a DbContext may work with an in-memory db, but what happens when they're used together? – Panagiotis Kanavos Aug 25 '21 at 13:50
  • I totally understand that. Since there's no information on that, I've covered unit/integration tests in my answer. And I don't think it is wrong – David Oganov Aug 25 '21 at 13:54
  • @PanagiotisKanavos, if they are used together then you are stepping into integration tests area where you are not testing a unit, but a component as a whole testing how it works with its dependencies. The purpose of unit testing is to test isolated unit w/o affect of its dependencies' behavior. But it's out of topic discussion – David Oganov Aug 25 '21 at 13:58
  • And both kinds of test can be run with XUnit. Besides, what is a `Unit`. It differs not only between people, but from one test to the next. – Panagiotis Kanavos Aug 25 '21 at 14:05
  • Right now the question is "where is my connection string?" not whether the test runner should be used to run integration tests. `Don't use a connection string` isn't an answer to that question – Panagiotis Kanavos Aug 25 '21 at 14:07
  • All the integration tests in [Integration tests in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-5.0) use XUnit. Even automated acceptance tests use XUnit to drive Selenium or Playwright and check how an actual web site behaves, whether there are any errors or whether a specific button has the correct style – Panagiotis Kanavos Aug 25 '21 at 14:10
  • I believe we are on the same page, you probably just got me wrong. I'm not saying not to use connection string in general. The second part of my answer says that you may want to make your components configurable to be able to pass the connection string. When writing unit tests though, you should NOT interact with database. – David Oganov Aug 26 '21 at 06:45
0

I haven't used xUnit test. But I guess, it might help you. From the comment I've seen you've created a startup.cs. In your startup.cs you can configure similar to this (this code is from .Net Core 5)

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            // DatabaseContext comes from the folder/Data/DatabaseContext.cs
            services.AddDbContext<DatabaseContext>(options => 
                options.UseSqlServer(Configuration.GetConnectionString("sqlConnection")) // sqlConnection comes from appSettings connectionString
            );

 services.AddControllers()
}

And in your appsettings.json

"ConnectionStrings": {
    "sqlConnection": "Server=YourDbServer;Database=yourdatabasename;Integrated Security=true; Trusted_Connection=True;"

  },

You can use localdb. And to find the DB server, you can check out this answer: Visual Studio Code Map: Unable to connect to the specified database

InsParbo
  • 390
  • 2
  • 13