I am trying to run several unit tests (by pressing Run All in Visual Studio). When I run all using Test>Run>All Tests the test below fails, but when I run the individual test (by right clicking the test itself in test Explorer) it succeeds. I believe I have my Setup and TearDown configured wrong, but I'm not sure whats missing.
Error:
Message: System.ArgumentException : An item with the same key has already been added. Key: System.Object[]
My Code:
[TestFixture]
public class GCLandingUserModelTest
{
DbContextOptions<GreenCardContext> gcopt = new DbContextOptionsBuilder<GreenCardContext>()
.UseInMemoryDatabase(databaseName: "GreenCardSite").Options;
private GreenCardContext _mockGC;
[SetUp]
public void InitData()
{
_mockGC = new GreenCardContext(gcopt);
}
[TearDown]
public void ClearData()
{
_mockGC = null;
}
[Test]
public void TestAddObjMoq()
{
// Insert seed data into the database using one instance of the context
_mockGC.UserRolePrice.Add(new UserRolePrice { LGID = 1, UserRoleId = -1 });
_mockGC.SaveChanges();
Assert.AreEqual(1, _mockGC.UserRolePrice.ToList().Count, $"UserRolePrice not added to context");
//verify that obj created and changes were saved on the Mock Context
int objlgid = _mockGC.UserRolePrice.Where(x => x.UserRoleId == -1).Select(x => x.LGID).First();
Assert.AreEqual(1, objlgid,$"The returned lgid was: {objlgid}");
}
}
Maybe you need to clear the in-memory datastore.
_mockGC.Database.EnsureDeleted();
DatabaseFacade.EnsureDeleted Method
Ensures that the database for the context does not exist. If it does
not exist, no action is taken. If it does exist then the database is
deleted.
Warning: The entire database is deleted, and no effort is made to
remove just the database objects that are used by the model for this
context.
Related
I am currently developing an app store style API which has the following entities (plus many others, but not relevant to the problem):
App (1 to many relationship to AppRevision - contains IEnumerable property)
AppRevision
Installation
I have come across an odd problem where the behaviour of EF differs in unit tests to when actually running the API, in that navigation properties are automatically being included when unit testing.
Take the following code snippet from my command handler:
App app = await this.context.Apps
.Include(a => a.Installations)
.FirstOrDefaultAsync(a => a.Id == command.AppId);
if (app != null) {
// Code omitted for brevity
}
When running the API, if I inspect app after this code has been run, the AppRevisions collection on the App entity is empty, as you would expect as I have not expliclity told EF to .Include(a => a.AppRevisions) - the API then throws an exception when trying to process code later on that needs this data to be there.
Now look at the following unit test for the same handler:
[Fact]
public async void Handle_ShouldAddInstallationRecord_WhenDataIsValid()
{
Guid testGuid = Guid.NewGuid();
CreateInstallationCommand command = new CreateInstallationCommand(testGuid, "ABC", "abc#abc.com", null);
using (TestContext context = new TestContextFactory().CreateTestContext())
{
context.Apps.Add(new App() { Id = testGuid });
context.AppRevisions.Add(new AppRevision() { Id = Guid.NewGuid(), AppId = testGuid, Status = AppRevisionStatus.Approved, IsListed = true });
await context.SaveChangesAsync();
CreateInstallationCommandHandler handler = new CreateInstallationCommandHandler(context);
CommandResult result = await handler.Handle(command, new CancellationToken());
Assert.True(result);
Assert.Single(context.Installations);
}
}
If I step through this test, when I get to the handler and inspect the app variable, the AppRevisions collection has automatically been populated. As a result, the test passes because the code that requires the AppRevisions collection to be populated can execute.
The expectation is that this test should actually fail, because I'm not telling EF to include those entities in the query.
I am using a Sqlite in memory database to create the database context for my unit tests and running .NET Core 2.2
I originally thought this was something to do with the changetracker. While disabling this does solve the immediate problem reported above, it creates a load of other problems so isn't a viable solution (and probably wouldn't be the correct one anyway)
Any suggestions gratefully received
For anyone who comes across this post in the future, the solution is as per the comments on the original question, to use separate contexts for seeding test data and getting the data later in the test:
[Fact]
public async void Handle_ShouldAddInstallationRecord_WhenDataIsValid()
{
Guid testGuid = Guid.NewGuid();
CreateInstallationCommand command = new CreateInstallationCommand(testGuid, "ABC", "abc#abc.com", null);
using (TestContextFactory contextFactory = new TestContextFactory())
{
using (TestContext seedContext = contextFactory.CreateTestContext())
{
seedContext.Apps.Add(new App() { Id = testGuid });
seedContext.AppRevisions.Add(new AppRevision() { Id = Guid.NewGuid(), AppId = testGuid, Status = AppRevisionStatus.Approved, IsListed = true });
await seedContext.SaveChangesAsync();
}
using (TestContext getContext = contextFactory.CreateTestContext())
{
CreateInstallationCommandHandler handler = new CreateInstallationCommandHandler(getContext);
CommandResult result = await handler.Handle(command, new CancellationToken());
Assert.True(result);
Assert.Single(getContext.Installations);
}
}
}
I've inherited some tests with this project. They were working when running against the SQL database, but slowly. I'm trying to switch over to using Effort.
.NET4.5, EF6.2, Effort 1.3.10.
I have two possibly related issues with my unit tests.
It doesn't matter if I run the tests in parallel or not.
1) If I run more than one at a time, I get
Saving or accepting changes failed because more than one entity of type 'Center.Shared.Person' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration. ---> System.InvalidOperationException: Saving or accepting changes failed because more than one entity of type 'Center.Shared.Person' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration..
So it appears that the tests are not properly isolated.
Tracing through the code, I can see that CreateTransient is called, but it apparently isn't transient enough.
public DbConnection CreateConnection(string nameOrConnectionString)
{
lock (_lock)
{
if (_connection == null)
{
_connection = Effort.DbConnectionFactory.CreateTransient();
}
return _connection;
}
}
In the TestInitialize routine I try to reset the database.
[TestInitialize]
public override void Initialize()
{
db.Database.Delete();
db.Database.CreateIfNotExists();
db.Database.Initialize(true);
This is highly convoluted code, so if we need to post more code, it's a long time before we get to the bottom of the rabbit hole. Probably better to create a PoC.
2) If I run the tests independently, I get a different problem. Again, these passed against SQL but not Effort.
[TestMethod]
public void ClientAccessorTests_Find()
{
Client result;
Client client = new Client()
{
Complete = false,
HeadOfHousehold = true,
PersonID = _person.PersonID
};
_accessor.Create(client, _accessor.DefaultConnectionContext);
result = _accessor.Find(new object[] { client.ClientID }, _accessor.DefaultConnectionContext);
Assert.IsNotNull(result); // Fails with Assert.IsNotNull failed.
}
Create consists of
public virtual EntityType Create(EntityType entity, ConnectionContext connectionContext)
{
IsContextValid(connectionContext);
if (entity == null) throw new ArgumentException("", "entity");
using (var db = CreateDbContext<DbContextType>(connectionContext))
{
db.Set<EntityType>().Add(entity);
db.SaveChanges();
}
return entity;
}
Find consists of
public virtual EntityType Find(object[] primaryKey, ConnectionContext connectionContext)
{
IsContextValid(connectionContext);
if (primaryKey == null || primaryKey.Length == 0) throw new ArgumentException("", "primaryKey");
using (var db = CreateDbContext<DbContextType>(connectionContext))
{
return db.Set<EntityType>().Find(primaryKey);
}
}
I know it is calling CreateDbContext, but tracing into the code, as far as I can tell it appears to be the same database with the same ID.
What is it that should cause the tests to be isolated?
And any ideas on why Find would quit working when using an in-memory database?
I was trying to use the implicit approach, where everything is hooked up via settings in the app.config file.
I started having better luck once I abandoned that approach and created the database connection and set it explicitly.
System.Data.Common.DbConnection connection = new EffortProviderFactory("").CreateConnection("");
_accessor = new ClientAccessor();
_accessor.Connection = connection;
db = new EntitiesDb(connection);
The base Accessor creates copies of the DB at every turn, which is fine so long as it uses the same DbConnection. So I set that on the accessor and then use it here:
if (_connection == null) { // this is the path for the application
if (connectionContext == null) {
ret = new T();
} else {
ret = (T)Activator.CreateInstance(typeof(T), new object[] { connectionContext });
}
} else { // this is the path for unit tests.
ret = (T)Activator.CreateInstance(typeof(T), new object[] { _connection });
}
Finally, I had to add a constructor that took a DbConnection to DbContext and its decendants.
public EntitiesDb(DbConnection connection) : base(connection) { }
'Find' now works and the tests don't interfere with each other.
Next step is pushing this all down to the base classes.
I've noticed that NUnit's TestContext.CurrentContext.Result.Outcome.Status is always Inconclusive at the end of a test run. The CurrentContext is also unaware that any assertions have taken place.
Is it possible to get the status of a test before the [TearDown]?
I was hoping to use the value during the Dispose() of my test management class to capture metrics and other data for post-test diagnosis.
Code example from a new .NET Framework 4.6.1 project that only has the NuGet packages NUnit and FluentAssertions:
namespace TestProject
{
using FluentAssertions;
using NUnit.Framework;
[TestFixture]
public class Class1
{
[Test]
public void test1()
{
var a = 1;
var b = 2;
var c = 1;
var context = TestContext.CurrentContext;
a.Should().Be(c);
Assert.AreEqual(a, c);
}
}
}
A test result starts out as Inconclusive. If the test is skipped or ignored, then the result changes, but of course it is never executed.
If it is executed, the Outcome is Inconclusive until the test is over. Clearly, while you are still executing the test, it is not yet finished. When teardown begins, the outcome of the test is known, so it will vary according to whether the test method itself was successful. Of course, an exception in teardown may change the result to an Error state.
Bottom line, the Outcome field is not useful while the test method itself is still running. In any case, if you are executing code in the test method, the test has not yet failed. Otherwise, you would not have continued execution!
You say you can't use TearDown but the link you provide doesn't deal with the issue of accessing the test result. Can you explain further? What exactly do you want to do after checking the test result?
You could try using AssertionScope along with the AssertionScope.Succeeded flag. Although the intelisense on that property specifies:
Gets a value indicating whether or not the last assertion executed through this scope succeeded.
Example usage of AssertionScope
[Test]
public void Test()
{
var a = 1;
var b = 2;
var c = 1;
var context = new AssertionScope();
try
{
throw new Exception("Test");
}
catch (Exception e)
{
context.FailWith(e.ToString());
}
var strings = context.Discard();
Console.WriteLine(strings.StringJoin(","));
context.Succeeded.Should().BeFalse();
var someOtherContext = new AssertionScope();
try
{
c.Should().Be(a);
}
catch (Exception e)
{
someOtherContext.FailWith(e.ToString());
}
var discard = someOtherContext.Discard();
Console.WriteLine(discard.StringJoin(","));
someOtherContext.Succeeded.Should().BeTrue();
}
I'm applying NUnit integration tests on our controller endpoints in a .NET Web API 2 project whose models and controllers are generated via Entity code first from database.
I'm having trouble thinking of what parts of the controller I should test. In the end, we'd just like to be able to automate "can a user with "x" role get this data?"
Looking in the GET portion of this controller, what parts would you test and what's your reasoning?
namespace api.Controllers.myNamespace
{
public class myController : ApiController
{
private string strUserName;
private string strError = "";
private string strApiName = "myTable";
private myDatabase db = new myDatabase();
// ----------------------------------------------------------------------
// GET: api/path
public IQueryable<myTable> GetmyTable()
{
try
{
this.strUserName = this.getUserName();
if
(
// ----- authorize -----
db.view_jnc_role_api_permission.Count
(
view =>
(
view.permission == "get"
&& view.apiName == this.strApiName
&& view.userName == this.strUserName
)
) == 1
// ----- /authorize -----
)
{
// ----- get -----
IQueryable<myTable> data =
from tbl in db.myTable
where tbl.deleted == null
select tbl;
// ----- /get -----
return data;
}
else
{
strError = "Unauthorized.";
throw new HttpResponseException(HttpStatusCode.Forbidden);
}
}
catch (Exception ex)
{
if (strError.Length == 0)
{
if (this.showException())
{
strError = ex.ToString();
}
}
throw new HttpResponseException(ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, strError));
}
}
}
For reference, here's what I have so far. Some of these private fields I'm defining shouldn't be here - currently trying to get access to private methods from my test project via AssemblyInfo.cs to fix this:
namespace api.myNamespace
{
[TestFixture]
public class myController : ApiController
{
private string strUserName;
private string strError = "";
private string strApiName = "myTable";
private myDb db = new myDb();
// Using TransactionScope to (hopefully) prevent integration test's changes to database from persisting
protected TransactionScope TransactionScope;
// Instantiate _controller field
private myController _controller;
[SetUp]
public void SetUp() {
TransactionScope = new TransactionScope(TransactionScopeOption.RequiresNew);
// It's possible that one test may leave some state which could impact subsequent tests - so we must reinstantiate _controller at the start of each new test:
_controller = new myController();
}
[TearDown]
public void TearDown()
{
TransactionScope.Dispose();
}
**//------ TESTS -------//
// CanSetAndGetUserName
// AuthorizedUserCanGetData
// UnauthorizedUserCannotGetData
// AuthorizedUserCanPutData
// UnauthorizedUserCannotPutData
// AuthorizedUserCanPostData
// UnauthorizedUserCannotPostData
// AuthorizedUserCanDeleteData
// UnauthorizedUserCannotDeleteData**
[Test]
public void CanGetAndSetUsername()
{
// ARRANGE
var user = _controller.getUserName();
// ACT
// ASSERT
Assert.That(user, Is.EqualTo("my-internal-username"));
}
[Test]
public void UnauthorizedUserCannotGetData()
{
var user = "Mr Unauthorized";
// Unfinished bc integration testing is super abstract, subjective, hard, time consuming and hard. All downvoters are plebs.
Assert.That(user, Is.EqualTo());
}
}
}
}
integration tests means several things:
you setup your test data in the database, via a script for example.
you call the endpoint under test knowing exactly what data you should call it with and what you should get. This is all based on your test data you setup in step 1.
you compare your expected data with the one you got back.
this is an integration test as it touches everything, both api and database.
Now, you said you are having trouble deciding which parts of the controller to test. This suggests you are confusing integration tests with unit tests.
Integration tests we already covered.
Unit tests cover parts of functionality. You do not test controllers, forget about that.
What you really need to consider doing is this:
First, separate your code from the controller. Keep the controller very basic. It receives a call, validates the request model and passes it further to a class library where the functionality happens. This allows you to forget "testing the controller" and focus on your functionality instead. Unit tests will help here and your test cases will become something like this
I have a user, set up in a certain way.
I have some data, set up in a certain way
When I call method X, then I should get this response.
With such a setup in place, you can set your test data any way you like and check every single test case.
The only reason you wonder how you test your controller is because you dumped all your code into it, which of course makes everything hard. Think SOLID, think SOC ( Separation of concerns ).
One piece of advice: never ever return IQueryable from an endpoint, that's not data, that simply a query that hasn't run yet. Return a List, IEnumerable, an singular object, whatever you need, just make sure you execute that first by calling ToList() for example on your IQueryable expression first.
So, the steps are like this:
Setup your IQueryable first
Execute it by calling ToList(), First(), FirstOrDefault() whatever is appropriate and return the result of that.
I experience a strange issue while testing my Nhibernate repositories.
I have 10 unit-tests like the ones below. Everytime a run them in a batch the first fails and the rest succeeds. If a run them one by one they all fail. If a restart MSDTC before my testrun it sometimes behaves like before and sometimes all tests succeeds. I canĀ“t find a pattern why it behaves like that.
I want the transaction to rollback so that a start with a clean DB for every test, therefore the transaction disposal.
The test/tests are failing due to this error:
System.Data.SqlClient.SqlException: MSDTC on server 'MYCOMPUTERNAME\SQLEXPRESS' is unavailable.
My tests looks like this:
[TestInitialize]
public void MyTestInitialize()
{
_transactionScope = new TransactionScope();
}
[TestCleanup]
public void MyTestCleanup()
{
if (_transactionScope != null)
{
_transactionScope.Dispose();
_transactionScope = null;
}
}
[TestMethod]
[TestCategory("RepositoryTests")]
public void RepositoryCanSaveAProduct()
{
var platform = ProductObjectMother.CreatePlatform("100010", "Supplier 10");
var mainsegment = ProductObjectMother.CreateMainSegment("123");
var application = ProductObjectMother.CreateApplication("Foo");
var productfamily = ProductObjectMother.CreateProductFamily("X99");
Engine i = ProductObjectMother.CreateEngine(platform, productfamily, application, mainsegment);
var repository = new ProductRepository();
repository.Save(i);
repository.Flush();
}
Problem seems to be with transaction that is neither committed using _transactionScope.Complete() or roll back by throwing exception.
Also I notice one strange thing, test usually fails or run successfully by "Assert" functions(equals, not equal, exists etc from assert) that is missing from your test. :)