TDD workflow best-practices for .NET using NUnit - c#

UPDATE: I made major changes to this post - check the revision history for details.
I'm starting to dive into TDD with NUnit and despite I've enjoyed checking some resources I've found here at stackoverflow, I often find myself not gaining good traction.
So what I'm really trying to achieve is to acquire some sort of checklist/workflow —and here's where I need you guys to help me out— or "Test Plan" that will give me decent Code Coverage.
So let's assume an ideal scenario where we could start a project from scratch with let's say a Mailer helper class that would have the following code:
(I've created the class just for the sake of aiding the question with a code sample so any criticism or advice is encouraged and will be very welcome)
Mailer.cs
using System.Net.Mail;
using System;
namespace Dotnet.Samples.NUnit
{
public class Mailer
{
readonly string from;
public string From { get { return from; } }
readonly string to;
public string To { get { return to; } }
readonly string subject;
public string Subject { get { return subject; } }
readonly string cc;
public string Cc { get { return cc; } }
readonly string bcc;
public string BCc { get { return bcc; } }
readonly string body;
public string Body { get { return body; } }
readonly string smtpHost;
public string SmtpHost { get { return smtpHost; } }
readonly string attachment;
public string Attachment { get { return Attachment; } }
public Mailer(string from = null, string to = null, string body = null, string subject = null, string cc = null, string bcc = null, string smtpHost = "localhost", string attachment = null)
{
this.from = from;
this.to = to;
this.subject = subject;
this.body = body;
this.cc = cc;
this.bcc = bcc;
this.smtpHost = smtpHost;
this.attachment = attachment;
}
public void SendMail()
{
if (string.IsNullOrEmpty(From))
throw new ArgumentNullException("Sender e-mail address cannot be null or empty.", from);
SmtpClient smtp = new SmtpClient();
MailMessage mail = new MailMessage();
smtp.Send(mail);
}
}
}
MailerTests.cs
using System;
using NUnit.Framework;
using FluentAssertions;
namespace Dotnet.Samples.NUnit
{
[TestFixture]
public class MailerTests
{
[Test, Ignore("No longer needed as the required code to pass has been already implemented.")]
public void SendMail_FromArgumentIsNotNullOrEmpty_ReturnsTrue()
{
// Arrange
dynamic argument = null;
// Act
Mailer mailer = new Mailer(from: argument);
// Assert
Assert.IsNotNullOrEmpty(mailer.From, "Parameter cannot be null or empty.");
}
[Test]
public void SendMail_FromArgumentIsNullOrEmpty_ThrowsException()
{
// Arrange
dynamic argument = null;
Mailer mailer = new Mailer(from: argument);
// Act
Action act = () => mailer.SendMail();
act.ShouldThrow<ArgumentNullException>();
// Assert
Assert.Throws<ArgumentNullException>(new TestDelegate(act));
}
[Test]
public void SendMail_FromArgumentIsOfTypeString_ReturnsTrue()
{
// Arrange
dynamic argument = String.Empty;
// Act
Mailer mailer = new Mailer(from: argument);
// Assert
mailer.From.Should().Be(argument, "Parameter should be of type string.");
}
// INFO: At this first 'iteration' I've almost covered the first argument of the method so logically this sample is nowhere near completed.
// TODO: Create a test that will eventually require the implementation of a method to validate a well-formed email address.
// TODO: Create as much tests as needed to give the remaining parameters good code coverage.
}
}
So after having my first 2 failing tests the next obvious step would be implementing the functionality to make them pass, but, should I keep the failing tests and create new ones after implementing the code that will make those pass, or should I modify the existing ones after making them pass?
Any advice about this topic will really be enormously appreciated.

If you install TestDriven.net, one of the components (called NCover) actually helps you understand how much of your code is covered by unit test.
Barring that, the best solution is to check each line, and run each test to make sure you've at least hit that line once.

I'd suggest that you pick up some tool like NCover which can hook onto your test cases to give code coverage stats. There is also a community edition of NCover if you don't want the licensed version.

If you use a framework like NUnit, there are methods available such as AssertThrows where you can assert that a method throws the required exception given the input: http://www.nunit.org/index.php?p=assertThrows&r=2.5
Basically, verifying expected behavior given good and bad inputs is the best place to start.

When people (finally!) decide to apply test coverage to an existing code base, it is impractical to test everything; you don't have the resources, and there isn't often a lot of real value.
What you ideally want to do is to make sure that your tests apply to newly written/modified code and anything that might be affected by those changes.
To do this, you need to know:
what code you changed. Your source control system will help you here at the level of this-file-changed.
what code is executed as a consequence of the new code being executed. For this you need either a static analyzer that can trace the downstream impact of the code (don't know of many of these) or a test coverage tool, which can show what has been executed when you run your specific tests. Any such executed code probably needs re-testing, too.
Because you want to minimize the the amount of test code you write, you clearly want better than file-precision granularity of "changed". You can use a diff tool (often build into your source control system) to help hone the focus to specific lines. Diff tools don't actually understand code structure, so what they report tends to be line-oriented rather than structure oriented, producing rather bigger diffs than necessary; nor do they tell you the convenient point of test access, which is likely to be a method because the whole style of unit test is focused on testing methods.
You can get better diff tools. Our Smart Differencer tools provide differences in terms of program structures (expressions, statements, methods) and abstracting editing operations (insert, delete, copy, move, replace, rename) which can make it easier to interpret the code changes. This doesn't directly solve the "which method changed?" question, but it often means looking at a lot less stuff to make that decision.
You can get test coverage tools that will answer this question. Our Test Coverage tools have a facility to compare previous test coverage runs with current test coverage runs, to tell you which tests have to be re-run. They do so by examining the code differences (something like the Smart Differencer) but abstract the changes back to method level.

Related

Methods of the class inherited from ITestListener are not started. NUnit

I have an implementation of the listener interface and I want to save a screenshot of the web driver, as well as the xml of the test itself.
public class TestListener : ITestListener
{
public void TestFinished(ITestResult result)
{
try
{
string fileName;
if (!Directory.Exists("Logs")) Directory.CreateDirectory("Logs");
if (Directory.GetFiles(#"Logs\").Length == 0)
{
fileName = #"Logs\Log_0.xml";
}
else
{
fileName = #"Logs\Log_" + System.Convert.ToString(
Directory.GetFiles(#"Logs\").Select(item => System.Convert.ToInt32(item.Split('_')[1]))
.OrderByDescending(item => item).First() + 1) + ".xml";
}
using (StreamWriter fstream = new StreamWriter(fileName))
{
fstream.Write(result.ToXml(true).OuterXml);
}
}
catch {}
if (result.ResultState.Status == TestStatus.Failed)
{
try
{
Screenshot screenshot = ((ITakesScreenshot)Driver.DriverInstance.GetInstance()).GetScreenshot();
if (!Directory.Exists("Screenshots")) Directory.CreateDirectory("Screenshots");
if (Directory.GetFiles(#"Screenshots\").Length == 0)
screenshot.SaveAsFile(#"Screenshots\Screen_0.jpg");
else
{
string fileName = #"Screenshots\Screen_" + System.Convert.ToString(
Directory.GetFiles(#"Screenshots\").Select(item => System.Convert.ToInt32(item.Split('_')[1]))
.OrderByDescending(item => item).First() + 1) + ".jpg";
screenshot.SaveAsFile(fileName);
}
}
catch {}
}
}
....
}
and I run a illustration test to show to see that it works
[Test]
public void test()
{
Assert.IsTrue(false);
}
but don't push a event for listener.
NUnit v3.12.0
As you discovered, the NUnit Framework has an interface ITestListener, which it uses to communicate events about tests. However, it isn't something you can create in your tests because you have no way to cause NUnit to call your methods with that interface.
You have probably confused this interface with ITestEventListener, which is an interface provided by the NUnit engine. The engine, of course, is a different piece of software. It's the software, which runs tests written against the NUnit framework. The engine allows several types of extensions to be created and one of those is, in fact, a test event listener extension, implementing ITestEventListener.
However, you can't implement an extension by including its code in your tests. (See note) Extensions are implemented separately and must be installed into the engine you are using... i.e. the engine that came with your test runner.
I can't write a complete how-to here for implementing engine extensions. I'll refer you to the docs for that...
https://docs.nunit.org/articles/nunit-engine/extensions/creating-extensions/Event-Listeners.html
If you look at the index on that page, you'll also see there is info about both framework and engine extensibility. Once you have absorbed it, I suggest another question for any problems you may run into.
Note: I wish it were possible to just include the class in your tests and have it added as a sort of adhoc extension! However, there are architectural reasons why that's next to impossible... for one thing, your tests may be targeting a completely different runtime from the engine itself!

Testing validations C#

Below I've pasted my code. I'm validating a measure. I've written code that will read a Linux file. But if I wanted to pass multiple file names here would this be possible? so for example instead of my test just validating one file could I do a loop so it could ready multiple files in one go.
Once the file is being read and proceeded I return actualItemData. In my next method, I want to make a call to this actualItemData so the data is published in my var actual
public string validateMeasurement
{
var processFilePath = **"/orabin/app/oracle/inputs/ff/ff/actuals/xx_ss_x.csv.ovr";**
var actualItemData = Common.LinuxCommandExecutor.
RunLinuxcommand("cat " + processFilePath);
**return actualItemData;**
}
public void validateInventoryMeasurementValue(string Data, string itemStatus)
{
var expected = '6677,6677_6677,3001,6';
**var actual = actualItemData);**
Assert.AreEqual(expected, actual);
}
It looks like you are using msunit. As far as I know it doesn't support test cases. If you were to use nunit you would be able to do this using the TestCase attribute.
[TestCase("myfile1.txt", "6677,6677_6677,3001,6")]
[TestCase("myfile2.txt", "1,2,3")]
public void mytest(string path, string expected)
{
var actual = Common.LinuxCommandExecutor.
RunLinuxcommand("cat " + path);
Assert.AreEqual(expected, actual);
}
Generally you don't want to write unit tests that cross code boundaries (read files, hit the database, etc) as these tests tend to be brittle and difficult to maintain. I am not sure of the aim of your code but it appears you may be trying to parse the data to check it's validity. If this is the case you could write a series of tests to ensure that when your production code (parser) is given a string input, you get an output that matches your expectation. e.g.
[Test()]
public void Parse_GivenValidDataFromXX_S_X_CSV_ShouldReturnTrue(string filename)
{
// Arrange
var parser = CreateParser(); // factory function that returns your parser
// Act
var result = parser.Parse("6677,6677_6677,3001,6");
// Arrage
Assert.IsTrue(result);
}

Fake HttpContext.Current.Server.MapPath for unit test

I have a test which fails on this line. I've figured out that it is because of the HttpContext inside of my GetProofOfPurchase method. Here is the line I'm failing on:
var image = Image.GetInstance(HttpContext.Current.Server.MapPath(GlobalConfig.HeaderLogo));
This is my test:
[Test]
public void GetProofOfPurchase_Should_Call_Get_On_ProofOfPurchaseRepository()
{
string customerNumber = "12345";
string orderNumber = "12345";
string publicItemNumber = "12345";
var result = new ProofOfPurchase();
this.proofOfPurchaseRepository.Expect(p => p.Get(new KeyValuePair<string,string>[0])).IgnoreArguments().Return(result);
this.promotionTrackerService.GetProofOfPurchase(customerNumber, orderNumber, publicItemNumber);
this.promotionTrackerRepository.VerifyAllExpectations();
}
The test fails on promotionTrackerService.GetProofOfPurchase line. How do I fake the HttpContext in this situation? I have searched Stack Overflow for similar issues to mine but I'm unable to get anything to work.
I've tried doing this:
var image = Image.GetInstance(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GlobalConfig.HeaderLogo));
But it fails saying:
System.Net.WebException : Could not find a part of the path 'C:\Images\HeaderLogo.png'.
From what I've read on Stack Overflow I shouldn't be using HttpContext.Current if I plan to unit test it, which is why I have tried using Path.Combine, but I'm unable to get that to work properly.
Can someone offer some guidance to what I need to do to get this unit test to work?
Thank you!
What I prefer to do when writing tests for code involving non-pure functions is to hide them behind, in simplest cases, plain old Func<string, string>:
class PromotionTrackerService
{
private readonly Func<string, string> imageMapper;
public PromotionTrackerService(Func<string, string> imageMapper)
{
this.imageMapper = imageMapper ?? HttpContext.Current.Server.MapPath;
}
public void GetProofOfPurchase()
{
var image = Image.GetInstance(imageMapper(GlobalConfig.HeaderLogo));
}
}
Now, your test does not look like a unit test -- it's more of an integration test, with all that file access and all.

How to structure my unit test?

I am new to unit test and wondering how to start testing. The application I am currently working on, does not have any unit test. It a winform application and I am only interested to test the data layer of this application.
Here is an example.
public interface ICalculateSomething
{
SomeOutout1 CalculateSomething1(SomeInput1 input1);
SomeOutout2 CalculateOSomething2(SomeInput2 input2);
}
public class CalculateSomething : ICalculateSomething
{
SomeOutout1 ICalculateSomething.CalculateSomething1(SomeInput1 input1)
{
SomeOutout1.Prop1 = calculateFromInput1(input1.Prop1, input1.Prop2);
SomeOutout1.Prop3 = calculateFromInput2(input1.Prop3, input1.Prop4);
return SomeOutout1;
}
SomeOutout2 ICalculateSomething.CalculateOSomething2(SomeInput2 input2)
{
SomeOutout2.Prop1 = calculateFromInput1(input2.Prop1, input2.Prop2);
SomeOutout2.Prop3 = calculateFromInput2(input2.Prop3, input2.Prop4);
return SomeOutout2;
}
}
I would like to test these two methods in the CalculateSomething. Those methods implementation are long and complicated. How should I structure my test?
I don't see a reason for not using a straight-forward unit test implementation. I'd start with a basic test method:
[TestMethod]
public void CalculateSomething1_FooInput
{
var input = new SomeInput1("Foo");
var expected = new SomeOutput1(...);
var calc = new CalculateSomething(...);
var actual = calc.CalculateSomething1(input);
Assert.AreEqual(expected.Prop1, actual.Prop1);
Assert.AreEqual(expected.Prop2, actual.Prop2);
Assert.AreEqual(expected.Prop3, actual.Prop3);
}
And then, as you add CalculateSomething1_BarInput and CalculateSomething2_FooInput, factor out some common code into helper methods:
[TestMethod]
public void CalculateSomething1_FooInput
{
var input = new SomeInput1("Foo");
var expected = new SomeOutput1(...);
var actual = CreateTestCalculateSomething().CalculateSomething1(input);
AssertSomeOutput1Equality(expected, actual);
}
As far as unit testing is concerned you would have to create the test methods for the functions that you want.
[TestMethod()]
public void CalculateSomething1()
{
// First we have to define the input for the fucntion
var input = new SomeInput1(); // Assumes your constructor creates the value for prop1 and prop2. Change as needed.
var classToBeTested = new CalculateSomething();
var output = classToBeTested(input);
// There are multiple ways to test if the outcome is correct choose the one that is correct for the method/output.
Assert.IsNotNull(output);
}
The method above would be in a unit test project and associated class file.
Some things to keep in mind when unit testing
Unit tests need to be independent
Long complicated code should be re-factored down into smaller units of code and tested.
Interfaces are an awesome way to remove dependencies. The use of interfaces allows concepts such as mocking. Mocking can be a little complicated at first so take your time when learning it. There are several mocking frameworks out there that can help a lot. i.e. RhinoMocks, Moq just to name a couple.
Those are explicitly implemented properties, so you have to use an interface reference to test them.
var input1 = new SomeInput1();
// setup required data in input1.
ICalculateSomething calculator = new CalculateSomething();
var output = calculator.CalculateSomething1(input1);
// Have assert statements on the properties of output to verify the calculation.
Don't use var for calculator, because that will give you a CalculateSomething reference and the interface methods are hidden.

Can I write code with an expiration date?

I just had this idea for something that I'd love to be able to use:
Let's say I have to fix a bug and I decide to write an ugly code line that fixes the immediate problem - but only because I promise myself that I will soon find the time to perform a proper refactoring.
I want to be able to somehow mark that code line as "Expired in" and add a date - so that if the code is compiled some time after that date there will be a compilation error/warning with a proper message.
Any suggestions? It must be possible to perform - maybe using some complicated #IF or some options in visual studio?
I'm using VS 2005 - mainly for C#.
Mark the code with the System.ObsoleteAttribute attribute, you'll get a compiler warning, which will nag you to fix the code
[Obsolete("You've an ugly hack here")]
public void MyUglyHack()
{
...
}
Alternatively . . .
Write your own attribute, passing it an expiration date on the constructor, in the constructor throw an exception if DateTime.Now >= expirationDate.
The compile will fail until you fix the code (or more likely increase the expiration date, or far more likely you just remove the Attribute.
oooohhh - this is 'orrible. try this for a giggle:
[AttributeUsage(AttributeTargets.All)]
public class BugExpiryAttribute : System.Attribute
{
// don't tell 'anyone' about this hack attribute!!
public BugExpiryAttribute(string bugAuthor, string expiryDate)
{
DateTime convertedDate = DateTime.Parse(expiryDate);
Debug.Assert(DateTime.Now <= convertedDate,
string.Format("{0} promised to remove this by {1}",
bugAuthor, convertedDate.ToString("dd-MMM-yyyy")));
}
}
then, decorate your method/class etc:
[BugExpiryAttribute("Jack Skit", "2011-01-01")]
public static void Main(string[] args)
{
...
}
... nasty :-)
[DISCLAIMER] - created in the name of academic interest, not production code finese!!
[edit] - just to clarify, code compiled and in production will continue to run on/after the 'bugExpriryDate'. only once the code is run in the compiler (on/after the date), will the warning message be raised (debug.assert). just thought it worth making that distinction - cheers MartinStettner.
[caveat] - if used in classes/methods etc would need to be read via reflection. however (and this is interesting) will work straight off in the compiler if used on sub Main(). how strange!! (thanks for the nod Hans...)
I think this is the reason Visual Studio has a Task List. Add the comment:
\\ TODO: Fix this spaghetti by 01APR11
and it will show up like this
.
the keywords are configurable from the options
You could write comment lines in the form
// Expires on 2011/07/01
and add a prebuild step which does a solution-wide replace of these lines by something like
#error Code expired on 2011/07/01
for all lines that contain a date before the current day. For this prebuild step you would need to write a short program (probably using regular expressions and some date comparision logic)
This step could also be performed by a VS macro, which allows for easier access to all files fo the solution but has the disadvantage that it must be installed and run on all VS installations where your project is compiled.
One more option if you have unit tests for your code you can time bomb the tests that verifies your fix. This way you don't introduce strange checks in your production code.
Also I think the best option if you have to put in hack (you've probably already spent enough time looking at it to fix properly... but still want a hack there) than open bug/create task/work item (whatever you use to track future work) and decide if you want to fix it later.
Well it doesn't do exactly what you're asking for but you could use a Debug.Assert() method call which would alert you (in Debug only) that the code has expired. One benefit would be that it wouldn't inadvertently affect your production code (compilation or execution) but would be sufficiently annoying in Debug for you to want to correct it.
// Alert the developer after 01/07/2011
Debug.Assert(Date.Now < new DateTime(2011, 7, 1))
With .NET 6+ this is quite simple when a source generator is used:
Source generator
[Generator]
public class ObsoleteFromDateSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var currentDate = DateTime.Now.Date;
ImmutableArray<AttributeSyntax> attributes = context.Compilation
.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes())
.Where((d) => d.IsKind(SyntaxKind.Attribute))
.OfType<AttributeSyntax>()
.Where(d => d.Name.ToString() == SourceGeneratorConstants.ObsoleteFromDateAttributeName)
.ToImmutableArray();
foreach (var attribute in attributes)
{
try
{
var semanticModel = context.Compilation.GetSemanticModel(attribute.SyntaxTree);
var argumentDate = attribute.ArgumentList?.Arguments.FirstOrDefault()?.Expression;
var argumentMessage = attribute.ArgumentList?.Arguments.Skip(1).FirstOrDefault()?.Expression;
if (argumentDate != null)
{
var date = DateTime.ParseExact(semanticModel.GetConstantValue(argumentDate).ToString(), SourceGeneratorConstants.ObsoleteFromDateAttributeDateFormat, CultureInfo.InvariantCulture);
string? message = null;
if (argumentMessage is not null
&& semanticModel.GetConstantValue(argumentMessage) is Optional<object> tmp
&& tmp.HasValue)
{
message = tmp.Value?.ToString();
}
var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ObsoleteFromDate(message, /*isWarning:*/ currentDate < date), attribute.GetLocation());
context.ReportDiagnostic(diagnostic);
}
else
{
throw new ArgumentNullException(paramName: "date");
}
}
catch (Exception ex)
{
var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ObsoleteFromDateError(ex.Message), attribute.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}
public void Initialize(GeneratorInitializationContext context)
{
}
}
Some constants
public static class SourceGeneratorConstants
{
public const string ObsoleteFromDateAttributeName = "ObsoleteFromDate";
public const string ObsoleteFromDateAttributeDateFormat = "yyyy-MM-dd";
}
Build diagnostics
public static class DiagnosticDescriptors
{
public const string Category = "BNX";
public const string ErrorTitle = "Source code error";
public static readonly Func<string?, bool, DiagnosticDescriptor> ObsoleteFromDate = (message, isWarning) => new DiagnosticDescriptor(
id: $"{Category}{SourceGeneratorConstants.ObsoleteFromDateAttributeName}",
title: "Obsolete code",
messageFormat: message ?? "Obsolete code, please review",
category: Category,
defaultSeverity: isWarning ? DiagnosticSeverity.Warning : DiagnosticSeverity.Error,
isEnabledByDefault: true);
public static readonly Func<string?, DiagnosticDescriptor> ObsoleteFromDateError = (message) => new DiagnosticDescriptor(
id: $"{Category}{SourceGeneratorConstants.ObsoleteFromDateAttributeName}",
title: ErrorTitle,
messageFormat: $"Unable to parse {SourceGeneratorConstants.ObsoleteFromDateAttributeName} attribute because of error: {message} Expecting the following syntax: [{SourceGeneratorConstants.ObsoleteFromDateAttributeName}(\"{SourceGeneratorConstants.ObsoleteFromDateAttributeDateFormat}\", \"message\")]",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
}
A custom attribute
/// <summary>
/// Triggers a build error at and after a specific system date. Source generators must be included in the project.
/// </summary>
public class ObsoleteFromDateAttribute : Attribute
{
public const string DateFormat = "yyyy-MM-dd";
/// <summary>
/// Build error message.
/// </summary>
public string Message { get; }
/// <summary>
/// System date at and after which the build error should occur.
/// </summary>
public string Date { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ObsoleteFromDateAttribute"/> class.
/// </summary>
/// <param name="date">Required. System date at and after which the build error should occur. Expected format: <see cref="DateFormat"/></param>
/// <param name="message">Optional. Build error message.</param>
public ObsoleteFromDateAttribute(string date, string message): base()
{
Date = date;
Message = message;
}
}
And finally a test:
// The following should result in a build error
[ObsoleteFromDate("2000-01-01", "We are waiting for some Jira thing 1 to complete")]
public class ObsoleteClass1
{
}
// The following should result in a build warning
[ObsoleteFromDate("3000-01-01", "We are waiting for some Jira thing 2 to complete")]
public class ObsoleteClass2
{
}
Without controlling the compiler (possible in the 5.0 timeframe with compiler as a service?), you are not going to have your code expire. You can mark the code as deprecated, or use the Obsolete attribute, or similar, to fire off a warning, but people can ignore warnings (many devs I have met have not learned the rule that warnings are errors).
I think it is a lot of work to try to protect people from themselves. It is even harder when you are protecting them from themselves in the future. Mark the code as a kludge and leave it at that.
Instead of embedding a time bomb, perhaps consider applying a BUGBUG: comment?
Rather than forcing you or someone else to fix code that may be kind of unsightly but works as expected down the road, you can just do a solution-wide search and find the ugly bits when you decide it's time to get down and refactor the really ugly stuff.
Track it in a bug instead. Then it can be properly scheduled and prioritized with other refactoring work.
TODO comments in code can have a tendency to be lost and forgotten. Throwing a compiler error after a particular date will likely lead to that date being pushed forward, or the comment/attribute removed.
I hope i can help with this. take 2 datetimepicker on tool box. And just convert 1 datetimepicker.
private void expired()
{
DateTime expired = DateTime.Parse(Convert.ToDateTime(datetimepicker1.Text).ToString());
DateTime compare = DateTime.Parse(Convert.ToDateTime(datetimepicker2.Text).ToString());
if(expired < compare)
{
MessageBox.Show("This product is expired!");
}
else
}
MessageBox.Show("This product is not expired");
{
}
Both TIME and DATE emit strings and, to my knowledge, there is no way to parse them out at the preprocessing stage.
There are a few methods you can easily do in code to ensure that the code at least warns you at run time. Including an assert is one way, putting in a code comment also works, but the way I handle it is through including a doxygen comment with a note explaining that the function contains a hack, bug, or performance issue that needs to be resolved. This ends up getting filtered by many programmers and is easily viewable on the website for myself or other people to fix.

Categories

Resources