Showing posts with label unitTesting. Show all posts
Showing posts with label unitTesting. Show all posts

Thursday, June 14, 2012

Learning MVC: Unit Testing Validation in MVC

This is a rather short post. A little "trick" is required to test validation. If the Create function is called directly from the unit test, and the entity is validated using DataAnnotation validation, the model binder will not be invoked and the validation will not take place. The test result, obviously, will not be the expected one. To deal with that, it is necessary to mimic the behavior of the model binder. The following test creates a ValidationContext and uses the DataAnnotations.Validator class to validate the model.

[TestMethod()]
public void ValidateNameIsTooShort()
{
 Ingredient ingredient = new Ingredient() { IngredientName = "a" };

 var validationContext = new ValidationContext(ingredient, null, null);
 var validationResults = new List<ValidationResult>();
 Validator.TryValidateObject(ingredient, validationContext, validationResults);

 string error = GetValidationError(validationResults);

 Assert.AreEqual(error, Constants.Constants.IngredientNameTooShort);
}

If any errors are caught by the validation, they will be added to the ModelState of the controller. To get it back and compare with my expected error message, I just need to retrieve the first error message from the list of ValidationResult.

public string GetValidationError(List<ValidationResult> results)
{
 foreach (var result in results)
 {
  if(!String.IsNullOrEmpty(result.ErrorMessage))
  {
   return result.ErrorMessage;
  }
 }
 return string.Empty;
}

References:

Testing DataAnnotation-based validation in ASP.NET MVC
Unit tests on MVC validation
by . Also posted on my website

Tuesday, June 12, 2012

Learning MVC: Unit Testing CRUD actions in MVC

Continuing the example with a recipe database, and having the most basic structure working (recipes belong to categories and consist of ingredients), it is time to remember about TDD (test driven development). To be honest, it is already too late, because in proper TDD tests are supposed to be writter before the code is. I'll do my best next time. This time, however, there are some simple tests I can think of which relate to the Ingredient entity

  • Test that an Ingredient can be inserted into the database
  • Test that an Ingredient can be edited
  • Test that an Ingredient can be deleted from the database
  • Test that an Ingredient can not be deleted if it is used by any Recipe
  • Test that an Ingredient with a name that is too short can not be created
  • Test that an Ingredient with a name that is too long can not be created
  • Test that an existing Ingredient can not be edited so that its name becomes too short
  • Test that an existing Ingredient can not be edited so that its name becomes too long

In fact, the last four tests are not related to database manipulations because the errors will be caught before the attempt to save data is made. It would be more appropriate to place them in a separate class, which will test the model and I'll return to them next time. The first four tests appear to test database operations.

Firstly, I would like to have each test start from a known state - for example, an almost empty database which has some minimal amount of seed data. To achieve that, I use the initializer class that inherits from DropCreateDatabaseAlways. For reference, here is the full listing - the only thing I care about is to override the Seed function. I will use the data to test that I can not delete the ingredient which is in use.

public class TestDatabaseInitializer : DropCreateDatabaseAlways<RecipesEntities>
{
 protected override void Seed(RecipesEntities context)
 {
  var category0 = new Category { CategoryName = "Mains", Description = "Main Dishes" };
  var category1 = new Category { CategoryName = "Desserts", Description = "Dessert Dishes" };
  var categories = new List<Category>() { category0, category1 };
  categories.ForEach(c => context.Categories.Add(c));

  var ingredient0 = new Ingredient { IngredientName = "Meat" };
  var ingredient1 = new Ingredient { IngredientName = "Fish" };
  var ingredient2 = new Ingredient { IngredientName = "Potato" };
  var ingredients = new List<Ingredient>() { ingredient0, ingredient1, ingredient2 };
  ingredients.ForEach(i => context.Ingredients.Add(i));

  var recipes = new List<Recipe>();
  recipes.Add(new Recipe { RecipeName = "Grilled fish with potatoes", Category = category0, Ingredients = new List<Ingredient>() { ingredient1, ingredient2 } });
  recipes.Add(new Recipe { RecipeName = "Grilled steak with potatoes", Category = category0, Ingredients = new List<Ingredient>() { ingredient0, ingredient2 } });
  recipes.ForEach(r => context.Recipes.Add(r));
 }
}

I added a small function to my test class to create a database.

[TestInitialize()]
public void SetupDatabase()
{
 Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
 Database.SetInitializer<RecipesEntities>(new TestDatabaseInitializer());
}

Now I think I'm ready to create a simple test.

[TestMethod()]
public void CreateTest()
{
 SetupDatabase();
 IngredientController target = new IngredientController();
 Ingredient ingredient = new Ingredient() { IngredientName = "test" };

 ActionResult actual = target.Create(ingredient);
 Assert.IsTrue(ingredient.IngredientID != 0);

 RecipesEntities db = new RecipesEntities();
 var newIngredient = db.Ingredients.Find(ingredient.IngredientID);
 Assert.AreEqual(ingredient.IngredientName, newIngredient.IngredientName);
}

I create the Ingredient, record it's IngredientID and make sure that I can retrieve it back from the database by ID after it's added. Deletion and editing tests are equally simple. Now the "negative" test: I'm not testing what I can do now, but rather what I can not do. I should not be able to delete the ingredient if it is used by any recipes - that would destroy referential integrity. Here is a simple test that passes:

[TestMethod()]
public void CanNotDeleteUsedIngredient2()
{
 SetupDatabase();
 IngredientController target = new IngredientController();
 RecipesEntities db = new RecipesEntities();
 var ingredient = db.Ingredients.Where(i => i.IngredientName == "Meat").FirstOrDefault();
 int id = ingredient.IngredientID;

 Assert.IsNotNull(ingredient);
 ActionResult actual = target.DeleteConfirmed(id);

 db = new RecipesEntities();
 var deletedIngredient = db.Ingredients.Find(id);
 Assert.IsNotNull(deletedIngredient);
}

Okay, I tried to delete the ingredient and then I verified and it is still in the database. That's expected, but that tells me nothing about the reason why the ingredient was not deleted. Maybe the whole database is offline or there is an uncommitted transaction. To improve the test, I would like to know something about the reason. I need to check for the errors in the ModelState. Fortunately, I can access the ModelState from the ActionResult. Here is what I could do to return the first error that is found in the ModelState:

public string GetFirstErrorMessage(ActionResult result)
{
 ViewResult vr = (ViewResult)result;

 foreach (ModelState error in vr.ViewData.ModelState.Values)
 {
  foreach (var innerError in error.Errors)
  {
   if (!string.IsNullOrEmpty(innerError.ErrorMessage))
   {
    return innerError.ErrorMessage;
   }
  }
 }
 return string.Empty;
}

Now I can modify the test to check the ErrorMessage. The check is rather lame at the moment - the error message is created dynamically to tell the user what recipes exactly use the ingredient. So I do not want to check the full error message and I'm satisfied with the fact that the first 10 characters are what I expect. Here is the slightly modified test:

[TestMethod()]
public void CanNotDeleteUsedIngredient()
{
 SetupDatabase();
 IngredientController target = new IngredientController();
 RecipesEntities db = new RecipesEntities();
 var ingredient = db.Ingredients.Where(i => i.IngredientName == "Meat").FirstOrDefault();
 int id = ingredient.IngredientID;

 Assert.IsNotNull(ingredient);

 ActionResult actual = target.DeleteConfirmed(id);
 Assert.AreEqual(GetFirstErrorMessage(actual).Substring(0, 10), "Cannot del");

 db = new RecipesEntities();
 var deletedIngredient = db.Ingredients.Find(id);
 Assert.IsNotNull(deletedIngredient);
}

References:

Code First Entity Framework Unit Test Examples
Exercise 2: Testing CRUD actions
How to get all Errors from asp.net mvc modelState?
How to get the Model from an ActionResult? by . Also posted on my website

Friday, December 11, 2009

Unit Testing With Compact Framework and Visual Studio

Following up my issue with running NUnit tests for the Windows Mobile application, I came across a couple of articles on using the unit testing framework integrated in Visual Studio 2008 which is now supposed to be user friendly.
The process starts with selecting the function name, right-clicking on it and selecting "Create Unit Tests"

I can select the functions I want unit tests to be created for - I'll only choose one for now

I am then prompted for the name of the project where my tests will be created. Visual Studio adds a new project to the solution and this is the code for the test method created for the function I chose.


///
///A test for CreateDatabase
///

[TestMethod()]
public void CreateDatabaseTest()
{
DataBase target = new DataBase(); // TODO: Initialize to an appropriate value
target.CreateDatabase();
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}

This is great, except that I want to test for the things I want to test. So, of course, I need to change that. That's probably closer to what I want to test in my method:


[TestMethod()]
public void CheckDatabaseCreation()
{
DataBase target = new DataBase();
target.SetFileName(@"\Program Files\TestDB\TTrack.sdf");
target.SetConnectionString(@"Data Source=\Program Files\TestDB\TTrack.sdf");
target.DeleteDatabase();
target.CreateDatabase();
target.RunNonQuery(target.qryInsertRecord);
int count = target.RunScalar(target.qryCountUsers);
Assert.AreEqual(count, 1);
}

This is not so much different from the way tests are created in NUnit. In fact, so far there is no difference at all. Now, to run the test. There is a menu item "Test" in the top menu where I can select Test->Windows->Test View and the "Test View" becomes visible.

There I can see my tests - the auto generated one and the one I added myself.


I can run all tests or select any combination of tests I want to run from the Test View and choose either "Run Selection" or "Debug Selection" (I did not find out yet what the difference is - if I place a breakpoint inside the test method and choose "Debug Selection", the execution does not break at the breakpoint). After the test(s) finished running, I can see the result in the Test Results window.

by . Also posted on my website

Tuesday, November 24, 2009

Compact Framework and NUnit

I have the idea of a small application I could write for the Windows Mobile. The application will only use its local SQL Server database, at least initially, and it is really simple to create a local database on the device. The only things I need are the physical location of the sdf file on the device and the connection string.

In my "DataBase" class I generate them

private string GetLocalDatabasePath()
{
string applicationPath = Path.GetDirectoryName(this.GetType().Assembly.GetName().CodeBase);
string localDatabasePath = applicationPath + Path.DirectorySeparatorChar +
"TTrack.sdf";
return localDatabasePath;
}

private string GetLocalConnectionString()
{
string localConnectionString = "Data Source=" +
GetLocalDatabasePath();

return localConnectionString;
}

To create a database I just check if the database file already exists, and if not - I create it. Also, for testing purposes, the delete database function is used.

internal void CreateDatabase()
{
if (!File.Exists(localDatabasePath))
{
using (SqlCeEngine engine = new SqlCeEngine(localConnectionString))
{
engine.CreateDatabase();
}

RunNonQuery(qryCreateTables);
}
}

internal void DeleteDatabase()
{
string dbPath = GetLocalDatabasePath();
if (File.Exists(dbPath))
{
File.Delete(dbPath);
}
}

The RunNonQuery bit is just the creation of tables in the database.

internal void RunNonQuery(string query)
{
string connString = GetLocalConnectionString();

using (SqlCeConnection cn = new SqlCeConnection(connString))
{
cn.Open();
SqlCeCommand cmd = cn.CreateCommand();
cmd.CommandText = query;
cmd.ExecuteNonQuery();
}
}

The query for now just creates the simplest possible "dummy" table

internal string qryCreateTables = "CREATE TABLE Users (" +
"UserID uniqueidentifier PRIMARY KEY DEFAULT NEWID() NOT NULL, " +
"Name NVARCHAR(50) NOT NULL )";

The RunScalar, obviously, is used to run ExecuteScalar(). Some refactoring still required to improve the code.

internal int RunScalar(string query)
{
string connString = GetLocalConnectionString();

using (SqlCeConnection cn = new SqlCeConnection(connString))
{
cn.Open();
SqlCeCommand cmd = cn.CreateCommand();
cmd.CommandText = query;
return int.Parse(cmd.ExecuteScalar().ToString());
}
}

Now that I can create a database, I can run this simple bit of code to see if it is working.

DataBase db = new DataBase();
db.CreateDatabase();
db.RunNonQuery(db.qryInsertRecord);
MessageBox.Show(db.RunScalar(db.qryCountUsers).ToString());

Database gets created, a record gets inserted, a messagebox with "1" is shown. All is well.

Next, I decide to quickly create and run a simple test for database creation: If the database is present, I delete it, then create a new one, insert one record and test for the count of records indeed being one.

Here is the test I write in NUnit.

[Test]
public void CheckDatabaseCreation()
{
DataBase db = new DataBase();
db.SetFileName(@"\Program Files\TestDB\TTrack.sdf");
db.SetConnectionString(@"Data Source=\Program Files\TestDB\TTrack.sdf");
db.DeleteDatabase();
db.CreateDatabase();
db.RunNonQuery(db.qryInsertRecord);
int count = db.RunScalar(db.qryCountUsers);
Assert.AreEqual(count, 1);
}

This does not go as well, however:

What happened there? Oh, of course - the NUnit test runs on the desktop, but the code is supposed to run on the emulator (I don't use the actual device yet). So, it looks like I will have to work on the approach to testing ...

by . Also posted on my website

Wednesday, September 16, 2009

A Small Unit Testing Gem

Since I started writing unit tests for my code, I had this question in mind. Let's say I have a project that is a class library. I have a class in that library and this class has some internal methods. Like this:

public class MyClass
{
public void MyPublicMethod
{
int k
// do something ...
int z = MyInternalMethod(k);
// do something else ...
}

internal int MyInternalMethod(int i)
{
// do something ...
}
}

Now I want to write unit tests for these methods. I would create a "UnitTests" project, reference the nunit.framework from it and write something like this:

[TestFixture]
public class UnitTests
{
private MyClass myClass;

[SetUp]
public void SetupTest
{
myClass = new MyClass();
}

[Test]
public void TestMyInternalMethod
{
int z = 100;
int k = myClass.MyInternalMethod(z); //CAN NOT DO THIS!
Assert.AreEqual(k, 100000);
}

[TearDown]
public void TearDown
{
myClass = null;
}
}

Of course, I can not do this, because of the MyInternalMethod scope. Today the StackOverflow guys pointed me to this little gem which is very helpful.

.Net Gem - How to Unit Test Internal Methods

Here's the short summary:

Go to the project that contains MyClass. Locate the AssemblyInfo.cs file. Add the following line to it:

[assembly: InternalsVisibleTo("UnitTests")]

Done!

by . Also posted on my website

Wednesday, July 15, 2009

Unit Testing - First Attempts

I had some time to use while I was investigating the smart card issues, so I decided to do the right thing. Something I have never done before. To learn how to write and use unit tests. Since I had a very small application that was testing the capabilities of the printer, it looked like a perfect guinea pig for my experiment. It turned out that writing tests is not so hard as I expected it to be. Well, I can not promise that I did it right, of course, because no one writes them here anyway and there is no one to mentor me or point to my mistakes.

So first of all I downloaded and installed NUnit framework

NUnit framework

Then I added a project of type class library to my solution and a single class called UnitTest to this solution. Here is the full code of the UnitTest class:

using System;
using NUnit.Framework;
using SmartCardTest;
using DataCardCP40;

[TestFixture]
public class UnitTest
{
public DataCardPrinter printer;
ICE_API.DOCINFO di;

[Test]
public void CreateObjects()
{
printer = new DataCardPrinter();
di = DataCardPrinter.InitializeDI();
printer.CreateHDC();
Assert.AreNotEqual(printer.Hdc, 0);
Assert.Greater(di.cbSize, 0);
}

[Test]
public void SetInteractiveMode()
{
int res = ICE_API.SetInteractiveMode(printer.Hdc, true);
Assert.Greater(res, 0);
}

[Test]
public void StartDoc()
{
int res = ICE_API.StartDoc(printer.Hdc, ref di);
Assert.Greater(res, 0);
}

[Test]
public void StartPage()
{
int res = ICE_API.StartPage(printer.Hdc);
Assert.Greater(res, 0);
}

[Test]
public void RotateCardSide()
{
int res = ICE_API.RotateCardSide(printer.Hdc, 1);
Assert.Greater(res, 0);
}

[Test]
public void FeedCard()
{
int res = ICE_API.FeedCard(printer.Hdc, ICE_API.ICE_SMARTCARD_FRONT + ICE_API.ICE_GRAPHICS_FRONT);
Assert.Greater(res, 0);
}

[Test]
public void SmartCardContinue()
{
int res = ICE_API.SmartCardContinue(printer.Hdc, ICE_API.ICE_SMART_CARD_GOOD);
Assert.Greater(res, 0);
}

[Test]
public void EndPage()
{
int res = ICE_API.EndPage(printer.Hdc);
Assert.Greater(res, 0);
}

[Test]
public void EndDoc()
{
int res = ICE_API.EndDoc(printer.Hdc);
Assert.Greater(res, 0);
}
}

There's not much to explain. First I create the objects required and verify that the device context was created and the DOCINFO struct was initialized. All the other tests just check the return codes of the printer functions. The error code is 0, so the check is for return value being greater than zero.

After compiling and fixing errors I realized that I have no way to set the sequence of the execution. Supposedly, as the theory teaches us, each test should be able to run alone and independent of whether the rest were passed, failed or run at all. Well, does not work so well in my case - if I want to test that the card can be ejected from the printer, I need to somehow insert it first! I found out, however, that the tests are executed in the alphabetic order of their names. Okay, that'll do for now. So I just renamed my tests like this A_CreateObjects(), B_SetInteractiveMode() etc. Then I compiled the solution, creating the "DataCardTest.dll". Next step is to run NUnit and open the dll. Wow! The smart thing can see all my tests now. When ready, just select Test->Run all from the menu and enjoy ...

It does not alway end that well, however - it might be like this (see how it tells what was the line where the error happened and how the expected test result was different from the actual).

What happened here? Took me some time to figure out ... the default printer was not set to my card printer.

by . Also posted on my website