I'm setting up integration tests and I'd like to use Jimmy Bogard's respawn library to intelligently clear out any data inserted during tests. My code is running against Azure databases.
When I run a test the relevant reset code is being hit (I set breakpoints and confirmed that) but the database isn't being cleared.
I confirmed that the connection string is correct.
I have no idea what else could be wrong, any insight would be very helpful!
Does the Respawn library work with Azure databases?
[Trait("Category", "Integration")]
public abstract class IntegrationTestBase : IClassFixture<ApiWebApplicationFactory>
{
protected readonly ApiWebApplicationFactory _factory;
protected readonly ITestOutputHelper _testOutputHelper;
protected readonly HttpClient _client;
private readonly Checkpoint _checkpoint = new ()
{
WithReseed = true,
};
public IntegrationTestBase(ApiWebApplicationFactory fixture, ITestOutputHelper testOutputHelper)
{
_factory = fixture;
_testOutputHelper = testOutputHelper;
_client = _factory.CreateClient();
_client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("IntegrationTest");
_testOutputHelper = testOutputHelper;
_checkpoint.Reset(_factory.Configuration.GetConnectionString("DB_CONN"));
}
}
Related
I have a dotnet core console application build to connect to a Sql Service Broker instance to monitor table changes.
The app monitors one table that is updated from an ERP system and then publishes messages to our bus.
It runs fine when running as a console application, or debugging in my IDE.
I am having an issue when using TopShelf to configure it as a windows service.
Here is the entry point:
private static void Main(string[] args)
{
RegisterComponents();
var serviceHost = HostFactory.Run(sc =>
{
sc.Service<ISalesOrderMonitorService>(s =>
{
var sqlListener = _container.ResolveNamed<SqlDependencyEx>(ListenerKey.SalesOrder);
var changeHandler = _container.Resolve<ISalesOrderChangeHandler>();
var listenerConfig = _container.ResolveNamed<ListenerConfiguration>(ListenerKey.SalesOrder);
var logger = _container.Resolve<ILogger<SalesOrder>>();
s.ConstructUsing(f =>
new SalesOrderMonitorService(sqlListener, changeHandler, listenerConfig, logger));
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
});
var exitCode = (int) Convert.ChangeType(serviceHost, serviceHost.GetType());
Environment.ExitCode = exitCode;
}
The "worker" class:
public abstract class ServiceBase<T, TZ> : IService<T>
where T : IChangeHandler
{
protected readonly IChangeHandler ChangeHandler;
protected readonly SqlDependencyEx Listener;
protected readonly ListenerConfiguration ListenerConfiguration;
protected readonly ILogger<TZ> Logger;
protected ServiceBase(SqlDependencyEx listener, IChangeHandler changeHandler,
ListenerConfiguration listenerConfiguration, ILogger<TZ> logger)
{
Logger = logger;
ListenerConfiguration = listenerConfiguration;
Listener = listener;
ChangeHandler = changeHandler;
}
public virtual void Start()
{
try
{
Listener.TableChanged += (o, e) => ChangeHandler.Process(e);
Listener.Start();
Logger.LogDebug(
$"Listening to changes on the {ListenerConfiguration.Table} table in the {ListenerConfiguration.Database} database");
}
catch (Exception e)
{
Logger.LogError(e, e.Message);
throw;
}
}
public virtual void Stop()
{
Listener.Stop();
}
Install through TopShelf is no problem:
c:>{ServiceName}.exe install -username "serviceAccount" -password "superSecret" -servicename "ServiceName" -servicedescription "Description" -displayname "Service DisplayName" --autostart
When I go to start the service - I get this:
This is misleading because the event viewer shows this:
This is happening way faster than 30 seconds. This is definitely related to how I am configuring TopShelf.
As stated - the application works just fine when run "debug" or even as just an exe console.
I got it figured out. Actually both comments from #DotNetPadawan and #Lex Li indirectly got me there.
For starters - enabling the remote debugger clued me in that my appsetting.json was not being read into my IConfiguration. That was really confusing because everything works fine running locally with a debugger or even just starting the exe.
The link Lex Li points out did not provide the answer - however that article had this reference:
Host and Deploy aspnetcore as a Windows Service
It was here that I found this little nugget:
The current working directory returned by calling GetCurrentDirectory for a Windows Service is the C:\WINDOWS\system32 folder. The system32 folder isn't a suitable location to store a service's files (for example, settings files). Use one of the following approaches to maintain and access a service's assets and settings files.
The link explains how to conditionally set the current directory if the app is running as a service.
var isConsole = args.Contains("-mode:console");
if (!isConsole)
{
var pathToExe = Process.GetCurrentProcess().MainModule?.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
Putting this out there for anyone else that runs into this problem.
Admittedly - netcore 3.0 is likely the better way to go - but I don't have the bandwidth to upgrade everything is this repo (lots of shared stuff) to 3.0. I needed to get this working.
[Route("api/[controller]")]
public class DigitalDocumentController : Controller
{
private IDigitalDocumentService digitalDocumentService;
private IDatabaseInitializer databaseInitializer;
public DigitalDocumentController(IDigitalDocumentService digitalDocumentService)
{
this.digitalDocumentService = digitalDocumentService;
}
public DigitalDocumentController(IDatabaseInitializer databaseInitializer)
{
this.databaseInitializer = databaseInitializer;
}
i want two controller constructor in my project to Mock in xUnit Testing, but there was an error in my swagger interface {
"error": "Multiple constructors accepting all given argument types have been found in type 'i2ana.Web.Controllers.DigitalDocumentController'. There should only be one applicable constructor."
}
can anybody help me how i can do it ?
…
what i am try to do , is to test Uniquness of the Name Field in my database
My testing code:
[Fact]
public void AddNotUniqueName_ReturnsNotFoundObjectResult()
{
var digitalDocument = new DigitalDocument
{
Image = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
CreatedOn = DateTime.Today,
Id = 6,
Location = "temp",
Name = "Flower",
Tages = new List<Tag> { new Tag { Id = 1, Value = "Tag 1" }, new Tag { Id = 1, Value = "Tag 2" } }
};
// Arrange
var mockRepo = new Mock<IDatabaseInitializer>();
mockRepo.Setup(repo => repo.SeedAsync()).Returns(Task.FromResult(AddUniqueDigitalDocument(digitalDocument)));
var controller = new DigitalDocumentController(mockRepo.Object);
// Act
var result = controller.Add(digitalDocument);
// Assert
var viewResult = Assert.IsType<NotFoundObjectResult>(result);
var model = Assert.IsAssignableFrom<int>(viewResult.Value);
Assert.NotEqual(6, model);
}
the "AddUniqueDigitalDocument" returns 6 only to test that the new digitaldocumet is not the same id of my initialize data.
When using dependency injection, you should only have one constructor where all dependencies can be satisfied. Otherwise, how is the DI container to know which constructor to utilize? That's your issue here. Using the Microsoft.Extensions.DependencyInjection package, and since this is a controller you're injecting into, there's only one reasonable way to solve this: don't register one or the other of the services, IDigitalDocumentService or IDatatabaseInitializer. If only one is registered, the service collection will simply use the constructor it has a registered service for.
It's possible with a more featured DI container, you might be able to configure something to allow it choose the proper constructor. How to do that would be entirely dependent on the DI container you end up going with, though, so not much more can be said on the subject at this point. Just realize that the default container (Microsoft.Extensions.DependencyInjection) is intentionally simplistic, so if you needs are more complex, you should sub in a full DI container.
UPDATE
You should be doing integration testing with the test host and an in-memory database. The basic approach is:
public MyTests()
{
_server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
_context = _server.Host.Services.GetRequiredService<MyContext>();
_client = _server.CreateClient();
}
In your app's Startup, create a virtual method:
public virtual void ConfigureDatabase(IServiceCollection services)
{
// normal database setup here, e.g.
services.AddDbContext<MyContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("Foo")));
}
Then, in ConfigureServices, replace your database setup with a call to this method.
Finally, in your test project, create a TestStartup class and override the ConfigureDatabase method:
public class TestStartup : Startup
{
public override void ConfigureDatabase(IServiceCollection services)
{
var databaseName = Guid.NewGuid().ToString();
services.AddDbContext<MyContext>(o =>
o.UseInMemoryDatabase(databaseName));
}
}
Now, in your tests you just make requests against the test client (which is just an HttpClient instance, so it works like any other HttpClient). You start by setting up your database with appropriate test data, and then ensure that the correct response is returned:
// Arrange
_context.Add(new DigitalDocument { Name = "Foo" });
await _context.SaveChanges();
// Act
// Submit a `DigitalDocument` with the same name via `_client`
// Assert
// Inspect the response body for some indication that it was considered invalid. Or you could simply assert that no new `DigitalDocument` was created by querying `_context` (or both)
This is admittedly a lot easier with an API, as with a web application, you're going to invariably need to do some HTML parsing. However, the docs and corresponding sample app help you with that.
Additionally, in actual practice, you'd want to use a test fixture to prevent having to bootstrap a test server for every test. Again, the docs have you covered there. One thing to note, though, is that once you switch to using a fixture, your database will then be persisted between tests. To segregate your test data, make sure that you call EnsureDeleted() on your context before each test. This can be easily done in the test class' constructor:
public class MyTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly MyContext _context;
public MyTests(WebApplicationFactory<Startup> factory)
{
factory = factory.WithWebHostBuilder(builder => builder.UseStartup<TestStartup>());
_client = factory.CreateClient();
_context = factory.Server.Host.Services.GetRequiredService<MyContext>();
_context.EnsureDeleted();
}
I don't even like this much bootstrapping code in my tests, though, so I usually inherit from a fixture class instead:
public class TestServerFixture : IClassFixture<WebApplicationFactory<Startup>>
{
protected readonly HttpClient _client;
protected readonly MyContext _context;
public TestServerFixture(WebApplicationFactory<Startup> factory)
{
factory = factory.WithWebHostBuilder(builder => builder.UseStartup<TestStartup>());
_client = factory.CreateClient();
_context = factory.Server.Host.Services.GetRequiredService<MyContext>();
_context.EnsureDeleted();
}
}
Then, for each test class:
public class MyTests : TestServerFixture
{
public MyTests(WebApplicationFactory<Startup> factory)
: base(factory)
{
}
This may seem like a lot, but most of it is one-time setup. Then, your tests will be much more accurate, more robust, and even easier in many ways.
I'm using NUnit 3 to do a global setup, which creates a local database needed to run several of my service tests, which looks like this:
[SetUpFixture]
public class FixtureSetup
{
private MobileServiceClient _client;
private SyncService _syncService;
[OneTimeSetUp]
public void GlobalSetup()
{
_client = Substitute.For<MobileServiceClient>(Settings.SyncUrl);
_syncService = Substitute.For<SyncService>(_client);
}
[OneTimeTearDown]
public void GlobalTeardown()
{
_syncService = null;
_client.Dispose();
}
}
Settings.SyncUrl contains the URL to Azure to which the Azure App Services SDK will by syncing eventually, and is not relevant to this question.
The one-time setup, simply constructs a new instance of the MobileServiceClient and passes that instance to my SyncService class, to construct the local store, which looks like this:
public class SyncService : ISyncService
{
private readonly IMobileServiceClient _client;
private MobileServiceSQLiteStore Store { get; }
public SyncService(IMobileServiceClient client)
{
_client = client;
Store = new MobileServiceSQLiteStore(Settings.SyncDb);
Store.DefineTable<User>();
_client.SyncContext.InitializeAsync(Store);
}
public async Task<List<TTable>> All<TTable>()
{
var table = await _client.GetSyncTable<TTable>().ToListAsync();
return table;
}
public async Task<TTable> Insert<TTable>(TTable table)
{
await _client.GetSyncTable<TTable>().InsertAsync(table);
return table;
}
public async Task<List<TTable>> Search<TTable>(Expression<Func<TTable, bool>> predicate)
{
var table = await _client.GetSyncTable<TTable>().Where(predicate).ToListAsync();
return table;
}
}
Settings.SyncDb simply points to the name of the db, called localstorage.db, and if on a mobile device, will store this in the application's file repository, on Windows or Mac, it will store it under the user's profile folder. Adding this just for reference.
My problem is that the global setup creates the localstorage.db correctly, but by the time the unit test runs, it cannot access the localstorage.db, because it's seemingly still in use by the global setup method.
I thought that reinstantiating the MobileServiceClient in the test class would resolve this, but it does not seem to do so. Is there a way that I can release the handle on the db, before hitting the unit test?
This is not an issue in development, as I can run the unit tests again after the first fail, but VSTS builds fail the test due to this reason.
Thanks in advance.
I am a rookie with ASP.NET currently developing monitoring system for SQL Server, and still working on retrieving job's information so I can show them on the page.
I tried to use Mr. Alexey Zimarev's code this code
And I wrote it on my controller like this
public class JobsController : Controller
{
static readonly string SqlServer = #"DESKTOP-PQD9KKN";
private ApplicationDbContext _context;
public JobsController()
{
_context = new ApplicationDbContext();
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
}
[Route("Customers/MigrateJob")]
public ActionResult MigrateJob()
{
ServerConnection conn = new ServerConnection(SqlServer);
Server server = new Server(conn);
JobCollection jobs = server.JobServer.Jobs;
foreach(Job job in jobs)
{
var jobactivity = new JobActivity
{
Name = job.Name,
IsEnabled = job.IsEnabled
....so on
};
_context.JobActivities.Add(jobactivity);
}
return RedirectToAction("List", "Jobs");
}
public ActionResult List()
{
var jobactivities = _context.JobActivities.ToList();
return View(jobactivities);
}
}
My approach is to store the job's information from SQL Server Agent to my JobActivity table using MigrateJob(). The problem is the job's information hasn't stored in my table yet without any error messages.
My Tools:
VS 2017
SQL Server ver 13
Any help would be appreciated :)
I have a VS solution with a "web" project (ASP.NET v5) and a "web.Tests" project (xunit.net 2.1beta) -- one of the tests is checking the rendered pages, and I'm trying to have the test bring up the site automatically, so I don't need to have it running separately/manually.
namespace web.Tests
{
public abstract class BrowserTest : IDisposable
{
protected readonly IisExpress server;
protected readonly IWebDriver driver;
protected BrowserTest()
{
var project = ProjectLocation.FromPath(Path.Combine(SolutionRoot, "src", "web", "wwwroot"));
var app = new WebApplication(project, 8080);
server = new IisExpress(app);
server.Start();
driver = new PhantomJSDriver();
}
public void Dispose()
{
server.Stop();
}
}
}
The server starts and stops fine, but I get an HTTP 500 when I hit a page, with a System.InvalidOperationException:
A type named 'StartupProduction' or 'Startup' could not be found in assembly 'web.Tests'.
How do I specify that I want to run Startup.cs from the "web" project, not the "web.Tests" project?
This was fixed by switching to Kestrel as the host -- especially since Kestrel is now the only supported host in ASP.NET 5
using System;
using System.Diagnostics;
using System.IO;
using OpenQA.Selenium;
using OpenQA.Selenium.PhantomJS;
namespace Test
{
public abstract class PhantomFixture : IDisposable
{
public readonly IWebDriver driver;
private readonly Process server;
protected PhantomFixture()
{
server = Process.Start(new ProcessStartInfo
{
FileName = "dnx.exe",
Arguments = "web",
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), "..", "Web")
});
driver = new PhantomJSDriver();
}
public void Dispose()
{
server.Kill();
driver.Dispose();
}
}
}
(obviously replacing the arguments in Path.Combine(...) with where your web app is located)
After a bit of trail and error with DotNet Core, here is what I came up with. Note that my pathing is a little different to yours as I have my test project separated from my web project.
private System.Diagnostics.Process _WebServerProcess;
[OneTimeSetUp]
public void SetupTest()
{
_WebServerProcess = new System.Diagnostics.Process
{
EnableRaisingEvents = false,
StartInfo = {
WorkingDirectory = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "MyWebProjectName"),
FileName = $"dotnet.exe",
Arguments = " run"
}
};
}
private void KillWebServer()
{
IEnumerable<Process> processes = Process.GetProcesses()
.Where(p => p.ProcessName == "MyWebProjectName.exe" && p.HasExited == false)
.AsEnumerable();
foreach (Process process in processes)
process.Kill();
if (_WebServerProcess != null)
{
if (!_WebServerProcess.HasExited)
_WebServerProcess.Kill();
_WebServerProcess = null;
}
}
public void Dispose()
{
KillWebServer();
}
Killing both the process that was started (eg, DotNet.exe & the webproject exe) seems be be the trick to ensuring that Kestral stopped.