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. :)
Related
I have a program, which is related to database. I need to unit-test some methods from class, which represents repository. I've decided to do it without localdb, but using Rollback attribute implementation:
public class Rollback : Attribute, ITestAction
{
private TransactionScope transaction;
public void BeforeTest(ITest test)
{
transaction = new TransactionScope();
}
public void AfterTest(ITest test)
{
transaction.Dispose();
}
public ActionTargets Targets => ActionTargets.Test;
}
I've got that from the Internet. This attribute implicitly begins transaction before method's code, and rolls it back after. That really works nice.
But at one moment I've wanted to debug this test:
[Test, Rollback]
public async Task AddingExistingDictionaryTypeThrowsExceptionTest()
{
await _repository.AddDictionaryType(tempDictionaryTypeName, tempDictionaryTypeDescription);
Assert.ThrowsAsync<Exception>(async () => { await _repository.AddDictionaryType(tempDictionaryTypeName, tempDictionaryTypeDescription); });
}
AddDictionaryType - is a method from repository. It adds new entity to database after checking, if such record doesn't exist already:
public async Task AddDictionaryType(string name, string description)
{
try
{
var sameNameCollection = _dbContext.DictionaryTypes.FromSqlRaw(#$"select * from dictionary_type where name = '{name}'");
var sameDescriptionCollection = _dbContext.DictionaryTypes.FromSqlRaw(#$"select * from dictionary_type where description = '{description}'");
if (sameNameCollection.Any() || sameDescriptionCollection.Any())
{
throw new AddingExistingDictionaryException();
}
_dbContext.Add(new DictionaryType(name, description));
await _dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
throw ex;
}
}
Don't pay attention to two SELECTS, I know, that I may make one check, but that's for my needs.
So, I've set the point after first call of AddDictionaryType method to check new record in database, after that I've done SELECT in SSMS(I know, that was silly, too, because method worked in transaction) from same table, in which I've tried to insert record. So, I've got an error. And now I will tell you about most interesting:
After that I can't normally execute the test, I always get error: "This connection was used with the ambient transaction. The original ambient transaction needs to be completed before this connection can be used outside of it".
So I think, that there is that transaction, which hasn't been closed.
The problem is that I cannot find it and kill.
I've operated with such queries to find it:
SELECT * FROM sys.sysprocesses;
SELECT * from sys.dm_tran_current_transaction with(nolock);
SELECT * from sys.dm_tran_active_transactions;
EXEC SP_who2;
SELECT * FROM sys. dm_exec_sessions;
SELECT * FROM fn_dblog(NULL, NULL);
I've seen processes, tried to kill them, didn't help.
I've reloaded server, didn't help.
There is no any information about such transaction in transaction log.
No I see, that testing like this is a big problem, because I don't even know, how that transaction could be named to rollback it by my hands.
May be it's not because of transaction? But I don't have any ideas. What about you?
I've refused using Rollback attribute, but follow
Charlieface's advice. Now I use using statement in each test, and everything works nice:
[Test]
public async Task Test()
{
using (var tran = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
// test logic
}
}
Also, I've understodd, that there is know any stuck transaction in database.
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.
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 have test like this. It's more functional test than unit test. The test are doing some rest request.
When test is running for long time, it is not canceled when timeout value pass but it is cancelled later(40 seconds instead of 1).
Same issue is happening when I use nunit3-console to run tests directly with --timeout=1000 param.
What is causing this issue? Can be done something about it?
[Test, Timeout(1000)]
[TestCase(TestEnvironments.PreIntegration, Category = TestConfigurationConstants.PREINTEGRATION)]
[TestCase(TestEnvironments.Integration, Category = TestConfigurationConstants.INTEGRATION)]
public void CreateAccount(TestEnvironments environment)
{
var testCase = testEnvironments[environment];
var userCredentials = new UserCredentials(settings.Default.AdminName, settings.Default.AdminPassword);
SetProtocol(testCase);
var adminToken = RestCommand.GetUserAccess(testCase.Keycloak, userCredentials).AccessToken;
CleanExistingTestUsers(testCase, adminToken);
var userResponse = RestCommand.CreateUser(testCase.Host, adminToken, configTests.GetTestUser(Name),
settings.Default.TestPassword);
Assert.AreEqual("0", userResponse.Error);
RestCommand.DeleteUser(testCase.Host, adminToken, userResponse.Id);
}
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.