I'm using Visual Studio 2010 Ultimate SP1. I have around 225 unit tests in my project, and they are all passing. One of those tests, however, reports 0% code coverage for the method it hits. When debugging, I can step through and see that it hits every line of the method. Yet in the code coverage report, it shows that the method isn't covered at all. All other tests and methods work just fine with regards to code coverage.
I've tried the following with no success:
Moved the method being tested into another class.
Made the method
static vs. non-static.
Moved the test to another class.
Deleted all of my .testsetting files and recreated them from scratch
Wrote a different test to exercise the same method, with the same results
restarted VS
rebooted
In case it matters, the method was in the Global.asax file. However, I moved it to another class and it made no difference.
Any ideas?
Here is the method being tested.
public void LogError(ILoggingService loggingService, IController controller)
{
if (loggingService == null)
throw new ArgumentNullException("loggingService");
if (controller == null)
throw new ArgumentNullException("controller");
Exception ex = Server.GetLastError();
loggingService.LogException(ex.Message, ex.StackTrace, ApplicationName.VoucherLog, UserInfo.UserName);
HttpException httpException = ex as HttpException;
if (httpException == null)
{
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = "HttpError";
Server.ClearError();
var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
controller.Execute(rc);
}
}
The first 4 lines, where the exceptions are thrown, are hit by other tests and show up in the code coverage statistics. The remainder of the method is hit by the following test (as verified by debugging through it and seeing that each and every line is, in fact, executed), but does not show up in code coverage stats. Here is the test:
[TestMethod]
[HostType("Moles")]
public void LogError()
{
DMVCommon.ApplicationName expectedApplication = DMVCommon.ApplicationName.VoucherLog;
string expectedUser = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
NotImplementedException expectedException = null;
System.Web.Moles.MHttpServerUtility.AllInstances.GetLastError = (System.Web.HttpServerUtility server) =>
{
return expectedException;
};
System.Web.Moles.MHttpApplication.AllInstances.ContextGet = (System.Web.HttpApplication application) =>
{
return MvcMockHelpers.FakeHttpCurrentContext();
};
try
{
throw new NotImplementedException();
}
catch (NotImplementedException exc)
{
expectedException = exc;
using (MvcApplication app = new MvcApplication())
{
bool called = false;
Mock<ILoggingService> mockLoggingService = new Mock<ILoggingService>();
mockLoggingService.Setup(a => a.LogException(expectedException.Message, expectedException.StackTrace, expectedApplication, expectedUser)).Callback(() => called = true);
Mock<IController> mockController = new Mock<IController>();
app.LogError(mockLoggingService.Object, mockController.Object);
mockController.Verify(a => a.Execute(It.IsAny<System.Web.Routing.RequestContext>()), Times.Exactly(1));
Assert.IsTrue(called);
}
}
}
This happens probably because of your usage of Moles - As the runner loads the assembly, Moles takes over and profiles the assembly instead of the coverage profiler.
There has been known integration issues with coverage tools and Moles -
In order for two .Net profilers (Moles & coverage) to work together they have to implement specific support for each other.
Try to run without Moles and see what happens...
Also see this as a similar example.
Related
I have a large proces that I need to debug and the proces could stop at anytime. I have configured Visual Studio 2017, to stop at any thrown exception, as in, even if it is handled, because I want to see what caused the exception. What I need is some sort of alarm when this happens, so that I can leave the program to run and then alert me if anything comes up. The only thing I have found is an alarm sound when a break point is hit, but it might not be a break point and I need more than a sound, I need to be able to execute some code, so that I can make my Phone go nuts or whatever. Is there any way I can trigger code when the debugger enters break mode?
Thanks in advance.
It is, using a VS package. You'll need to add this attribute on top of the class in order for code to run on package startup:
[ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids80.SolutionExists)] ///Able to run code on solution startup
Add these class values variables:
private DTE2 applicationObject;
private BuildEvents buildEvents;
private DebuggerEvents debugEvents;
then the following code can run:
protected override void Initialize()
{
base.Initialize();
applicationObject = (DTE2)GetService(typeof(DTE));
buildEvents = applicationObject.Events.BuildEvents;
debugEvents = applicationObject.Events.DebuggerEvents;
SetupEventHandlers();
}
And finally the code we have "all" being waiting for:
private void SetupEventHandlers()
{
//buildEvents.OnBuildDone += (scope, action) =>
//{
//};
debugEvents.OnEnterBreakMode += delegate (dbgEventReason reason, ref dbgExecutionAction action)
{
};
//var componentModel =
// GetGlobalService(typeof(SComponentModel)) as IComponentModel;
//if (componentModel == null)
//{
// Debug.WriteLine("componentModel is null");
// return;
//}
//var operationState = componentModel.GetService<IOperationState>();
//operationState.StateChanged += OperationStateOnStateChanged;
}
I'm using Visual Studio Ultimate 2013 and have a load test that uses a web test with a number of request and web test plugins.
In my PostRequest plugin, I'm checking the http status code of the response and am flagging an error in a WebTest.Context parameter, when a hhtp code of over 400 is returned. What I want to do is pick this up in the PostTransaction WebTest plugin and update a database table. The problem is, that the test aborts when the framework detects the error and the PostTransaction plugin isn't called.
I've added a PostWebTest plugin, that I thought would be called when the test iteration aborted, but it's never hitting this when a request fails. It does hit it if the test is successful. What am I missing?
public override void PostRequest(object sender, PostRequestEventArgs e)
{
...
statusCode = e.Response.StatusCode.GetHashCode();
If (statusCode > 400)
{
e.WebTest.Context["TransactionFailCount"] = 1;
}
}
public override void PostTransaction(object sender, PostTransactionEventArgs e)
{
int transactionFailCount = Convert.ToInt32(e.WebTest.Context["TransactionFailCount"]);
if (transactionFailCount > 0)
failCount = 1;
else
passCount = 1;
...
base.PostTransaction(sender, e);
}
public override void PostWebTest(object sender, PostWebTestEventArgs e)
{
base.PostWebTest(sender, e);
}
Thanks
A Web Performance Test (WPT) will continue to execute after an error is detected unless the Stop on error property of the test is true. It is possible that some aspects of a the handling of a request are not performed after an error, but I have not seen any such cases.
Be careful of terminology. WPTs use "transactions" as a way of grouping several requests. The context (right click) menu of a request has an "Add transaction" entry that selects a range of items in the test to be included in that transaction. Perhaps you should be using the PostRequest or PostPage plugins rather than PostTransaction.
I recommend that you do some experiments with a simple two or three request WPT plus some simple plugins that just announce that they have been called. For example
public override void PostRequest(object sender, PostRequestEventArgs e)
{
e.WebTest.AddCommentToResult("PostRequest called for url " + e.Request.Url);
}
Additionally, there is a good explanation of how and when plugins are called in pages 32 to 44 of the Visual Studio Performance Testing Quick Reference Guide from Codeplex.
The statement statusCode = e.Response.StatusCode.GetHashCode(); is strange. The value of e.Response.StatusCode is an enum, it integer value can be obtained by casting. The GetHashCode method is not intended to get the numeric value of an enum. The statement would be better as statusCode = (int)e.Response.StatusCode; or the assign and test (note that if is used for conditionals in C#, not If) might be better as
statusCode = e.Response.StatusCode;
if ( e.Response.StatusCode >= System.Net.HttpStatusCode.BadRequest ) { ... }
I have a simple method for sending emails:
public void notifyEmail(string messageSubject, string messageBody)
{
MailMessage message = new MailMessage(from, to);
message.Subject = messageSubject;
message.Body = messageBody;
SmtpClient client = new SmtpClient(smtp_client);
client.Send(message);
message.Dispose();//release everything related
}
And a unit test (I'm learning):
[TestMethod()]
public void notifyEmailTest()
{
eMail target = new eMail("TEST Subject","TEST Body"); // TODO: Initialize to an appropriate value
bool testSent = true;
try
{
target.notifyEmail();
}
catch (Exception)
{
testSent = false;
}
Assert.IsTrue(testSent);
}
I deliberately set the smtp_client variable value to something invalid.
Running the code in my project results in an error.
Running the test method results in a Pass. Should my test or method be structured differently so that errors will fail the test?
I always do everything I can to avoid putting try-catch clauses on my unit tests. Instead try using the ExpectedException attribute (the attribute is the same for NUnit and MSTest) and set the type to the exception you are expecting i.e.
[TestMethod]
[ExpectedException(typeof(NetworkException))]
public void ShouldThrowNetworkExceptionIfSmtpServerIsInvalid)
{
//... test code here.
}
Another approach that I have used is to create a static class with an AssertExpectedException method since sometimes a method can throw the same type of exception for different reasons and the only way to know for sure if the accurate message is being returned is with custom code since the attribute does not assert the message the thrown exception is returning.
Hope this helps.
Regards.
If you expect that target.notifyEmail() should be throwing an exception, then that's what you should be testing for. If you were using NUnit you could use Assert.Throws<T>, e.g.
[Test]
public void notifyEmailTestFails()
{
// TODO: Initialize to an appropriate value
eMail target = new eMail("TEST Subject","TEST Body");
Assert.Throws<InvalidOperationException>(target.notifyEmail());
}
However, now I see you're using VSUnit you should be using [ExpectedException(typeof(...))]
as mentioned in other answers.
In general you should have separate tests for success, failure, and for exception conditions.
The way I normally do this is to decorate the test with ExpectedException (
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute(v=vs.80).aspx)
. But you want to catch something MUCH less generic than "Exception."
If you don't want to use expected exception, then instead of:
bool testSent = true;
try
{
target.notifyEmail();
}
catch (Exception)
{
testSent = false;
}
Assert.IsTrue(testSent);
You can be a little less verbose:
try{
target.notifyEmail();
Assert.Fail("Expected an exception here");
}
catch (SmtpException){
}
I would highly recommend you to try the FluenAssertions:
http://fluentassertions.codeplex.com/
They are simple awesome and Elegant
And they let you check the exception message (You can not do that with the ExpectedException attribute)
Example:
using FluentAssertions;
[TestMethod]
public void notifyEmailTest()
{
eMail target = new eMail("TEST Subject","TEST Body"); // TODO: Initialize to an appropriate value
target.Invoking(x => x.notifyEmail())
.ShouldThrow<YourExcpectedException>()
.WithMessage("Your expected message", FluentAssertions.Assertions.ComparisonMode.Substring);
}
I'm creating a ASP.NET MVC 3.0 website, and have a couple of different database initializations based on whether the site is intended for development, testing, or production. I'm stuck on the testing initialization, as I'm trying to get a test user created. I can get the user to create just fine, however when I try to add some profile values, I get: System.Web.HttpException: Request is not available in this context. Is there a way to add Profile values in a situation where the request isn't going to be available?
Following code is what is being run:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
if (ApplicationServices.GetInitialCatalog() != "tasktracker")
{
Database.SetInitializer(new TaskTrackerDropCreateDatabaseIfModelChanges());
}
else
{
Database.SetInitializer(new TaskTrackerCreateDatabaseIfNotExists());
}
using (var db = new TaskTrackerContext())
{
db.Database.Initialize(false);
}
}
public class TaskTrackerDropCreateDatabaseIfModelChanges : DropCreateDatabaseIfModelChanges<TaskTrackerContext>
{
protected override void Seed(TaskTrackerContext context)
{
// Set up the membership, roles, and profile systems.
ApplicationServices.InstallServices(SqlFeatures.Membership | SqlFeatures.Profile | SqlFeatures.RoleManager);
// Create the default accounts and roles.
if (ApplicationServices.GetInitialCatalog() == "tasktracker_testing")
{
if (Membership.GetUser("testuser", false) == null)
{
Membership.CreateUser("testuser", "password", "testuser#test.com");
MembershipUser user = Membership.GetUser("testuser", false);
user.IsApproved = true;
var profile = ProfileBase.Create("testuser");
profile.SetPropertyValue("FirstName", "test");
profile.SetPropertyValue("LastName", "user");
profile.SetPropertyValue("TimeZone", "US Mountain Standard Time");
profile.Save();
}
}
}
}
Interesting question. Have you looked at using the new Universal Providers? Dunno if you will run into the same httpcontext issue but may be worth a look: http://www.hanselman.com/blog/IntroducingSystemWebProvidersASPNETUniversalProvidersForSessionMembershipRolesAndUserProfileOnSQLCompactAndSQLAzure.aspx
Did you try to do a call of "Initialize()" :
profile.Initialize(username, true)
after your create action to see if the context should be Initialized.
By using Reflector i saw the ProfileBase of Initialize (see below) creates this kind of context from the settings:
public void Initialize(string username, bool isAuthenticated)
{
if (username != null)
{
this._UserName = username.Trim();
}
else
{
this._UserName = username;
}
SettingsContext context = new SettingsContext();
context.Add("UserName", this._UserName);
context.Add("IsAuthenticated", isAuthenticated);
this._IsAuthenticated = isAuthenticated;
base.Initialize(context, s_Properties, ProfileManager.Providers);
}
It seems working here, the SettingsContext() seems taking account of my custom properties declared in the web.config.
Regards,
I come back again because the solution I added with the "Initialize()" function in fact not run really after an other test. So in fact I found a way which runs correctly.
The problem of "request is not available in this context" in application_start in your case could be due to the application mode "Integrated" which is new from II7 instead of the Classic mode.
To see a good explain you ca go on the Mike Volodarsky's blog IIS7 Integrated mode: Request is not available in this context exception in Application_Start .
I copy/paste an extract which could indicate the main reason:
" *This error is due to a design change in the IIS7 Integrated pipeline that makes the request context unavailable in Application_Start event. When using the Classic mode (the only mode when running on previous versions of IIS), the request context used to be available, even though the Application_Start event has always been intended as a global and request-agnostic event in the application lifetime. Despite this, because ASP.NET applications were always started by the first request to the app, it used to be possible to get to the request context through the static HttpContext.Current field.* "
To solve this you can use a workaround that moves your first-request initialization from Application_Start to BeginRequest and performs the request-specific initialization on the first request.
A good example of code is done in his blog :
void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
HttpContext context = app.Context;
// Attempt to peform first request initialization
FirstRequestInitialization.Initialize(context);
}
class FirstRequestInitialization
{
private static bool s_InitializedAlready = false;
private static Object s_lock = new Object();
// Initialize only on the first request
public static void Initialize(HttpContext context)
{
if (s_InitializedAlready)
{
return;
}
lock (s_lock)
{
if (s_InitializedAlready)
{
return;
}
// Perform first-request initialization here
//
// You can use your create profile code here....
//---
s_InitializedAlready = true;
}
}
}
We are getting the following warning from Code Analysis in Visual Studio 2010 and I'm wondering if this is a false positive that we can safely ignore or the code should be refactored to correctly dispose the object.
The relevant code:
public void MyFunction()
{
OracleConnection oraConnection = null;
OracleCommand oraCommand = null;
try
{
// Connect to the database
oraConnection = new OracleConnection(connectionString);
oraConnection.Open();
// Prepare and run the query
oraCommand = new OracleCommand(sqlQuery, oraConnection);
oraCommand.ExecuteNonQuery();
}
catch { throw; }
finally
{
// Perform a safe cleanup
if (oraCommand != null) { oraCommand.Dispose(); }
if (oraConnection != null)
{
oraConnection.Close();
oraConnection.Dispose();
}
}
}
The relevant error message:
Warning 18 CA2202 : Microsoft.Usage : Object 'oraConnection' can be disposed
more than once in method 'ClassName.MyFunction()'. To avoid generating a
System.ObjectDisposedException you should not call Dispose more than one
time on an object.
If you remove the line:
oraConnection.Close();
it should get rid of the warning, since closing and disposing a connection are essentially the same thing.
You might also want to replace your try/finally by a using statement.
Note that Microsoft's own guidelines say that IDisposable.Dispose should be implemented in such a way that it can safely be called multiple times. Which means that the CA2202 warning can safely be ignored, as noted in the comment by JoeM27 on the MSDN page for CA2202.