How can one do jQuery and C# unit testing of Editor input/output?

How can one do jQuery and C# unit testing of Editor input/output?

rdmrdm Posts: 192Questions: 54Answers: 4

To provide context, I use .NET MVC 5. To date, I've developed pages using Editor by "debug-oriented" approaches. In other words, use a page and do manual record entries, edits, and deletes. If it looks great, I'll hope it's all good until I get a bug report because a piece of something in the code either had an unknown side effect or an edge case appeared into the data set.

My Visual Studio solution contains my MVC application, my SQL Server database as a SQL Server Database Project, and a Unit Test project that tests all the functions in my database to make sure that all functions work as intended.

My first place to start would be trying to develop tests for the controller action for Editor.

[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult EditorTable()
{
    var settings = Properties.Settings.Default;
    var formData = HttpContext.Request.Form;    

    using (var db = new Database(settings.DbType, settings.DbAttendanceConnection))
    {
        var response = new Editor(db, "TableName", "Id")
            .Model<TableName>()
            .Field(new Field("FieldName"))
            /* more fields go here*/
            .Process(formData)
            .Data();

        return Json(response, JsonRequestBehavior.AllowGet);
    }
}

Using pseudocode, I would want to create tests like:

  • If Column A = "something" on the page, Column A should persist as "something"...
  • If Column B checkbox is clicked on the page, Column B should persist as "true".

Has anyone created any unit tests along these lines for an MVC project?

Replies

  • rdmrdm Posts: 192Questions: 54Answers: 4

    @Allan -- do you have anything in your library of unit tests related to the current scenario I might be able to use?

  • rdmrdm Posts: 192Questions: 54Answers: 4
    edited February 2018

    This is where I've encountered issues where unit tests would be invaluable.

    • var response = new Editor(db, "[tableName]", "Id").Process(formData)
  • allanallan Posts: 61,734Questions: 1Answers: 10,110 Site admin

    Rather than creating the Editor object in a chained style like in your original post, I'd suggest building them up in a more procedural style - for example:

    var editor = new Editor( db, "TableName", "Id" );
    editor.Field( new Field( ... ) );
    editor.Field( new Field( ... ) );
    // More fields (adding in a for loop for example)
    
    editor.Process( formData );
    
    var response = editor.Data();
    // ...
    

    Using that method you can add whatever fields you need using whatever conditional logic (if checkboxes are checked and submitted for example).

    Does that help some?

    Allan

  • rdmrdm Posts: 192Questions: 54Answers: 4

    Possibly. So are you suggesting it's the conditional logic that is tested and not the editor object?

  • allanallan Posts: 61,734Questions: 1Answers: 10,110 Site admin

    I may have misunderstood what you are looking for. From your original post I was thinking that you would be dynamically building a form that could be in a number of different configurations, then submit it and check that the server handles it correctly. If that was correct, then you'd need a way to build the Editor instance on the server-side based on how the client-side was sending data, which is what I addressed above.

    I get the impression I've misunderstood though!

    Allan

  • rdmrdm Posts: 192Questions: 54Answers: 4
    edited February 2018

    It might be helpful if I make my examples a little less generic. In the end, it might be beneficial if I make a "Hello Editor" project on GitHub. In the short term, consider this scenario.

    Here's a contrived C# entity model with varying data types. Suppose I wanted to test the behavior of Editor as it reads and persists data (depending on whether the controller action is acting as a get or a post).

    public class SampleTable
    {
        public int Id { get; set; }
    
        public DateTime SampleDate { get; set; }
    
        public DateTime? SampleNullableDate { get; set; }
    
        public bool SampleBool { get; set; }        
    
        public int SampleInt { get; set; }
    
        public int? SampleNullableInt { get; set; }
    
        [StringLength(4)]
        public string Sample4LengthString { get; set; }
    
        [StringLength(50)]
        public string Sample50LengthString { get; set; }
    }
    

    What would I want to unit test? Here are default examples I can pull from the top of my head. These are rough unit test concepts that would no doubt need to get refined as I get a better understanding of the mechanics of Editor. It's the insides of Editor I would want or need to test, but rather the interfaces of Editor (i.e., the input and output).

    At first these tests might seem very trivial and pedestrian, but that's the point. Only when the most obvious failure points are verified using static values (i.e., given input of X, I always get an output of X), I can then start looking at the less trivial testing points.

    • When user updates SampleDate in the View with date of '2015-01-05', Editor receives '2015-01-05' from jQuery.
    • When user updates SampleNullableDate in the View with date of '2015-01-05', Editor receives '2015-01-05' from jQuery.
    • When user updates SampleNullableDate in the View with null, Editor receives a null value from jQuery.

    And when I need to use SetFormatters or GetFormatters, the unit tests allow me to use a TDD approach to working out the correct formatters and verify that I have all the possible input possibilities covered.

    private static string WrapStringInQuotes(string input)
    {
        return @"""" + input + @"""";
    }
    
    [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
    public ActionResult EditorTable(string campus)
    {
        var settings = Properties.Settings.Default;
        var formData = HttpContext.Request.Form;
    
        using (var db = new Database(settings.DbType, settings.DbAttendanceConnection))
        {
            var response = new Editor(db, "ReconcileSummaryDaysAbsentAndReturned", "Id")
                .Model<ReconcileSummaryDaysAbsentAndReturned>()
                .Field(new Field("BatchDate").SetFormatter((val, data) => JsonConvert.DeserializeObject<DateTime>(WrapStringInQuotes(val.ToString()))))
                .Field(new Field("Reconciled"))
                .Field(new Field("Campus"))
                .Field(new Field("StudentName"))
                .Field(new Field("StudentId"))
                .Field(new Field("EntryDate").SetFormatter((val, data) => JsonConvert.DeserializeObject<DateTime>(WrapStringInQuotes(val.ToString()))))
                .Field(new Field("ExitDate").SetFormatter((val, data) => (string) val != ""
                    ? (object) JsonConvert.DeserializeObject<DateTime>(WrapStringInQuotes(val.ToString()))
                    : null))
                .Field(new Field("DateAttended").SetFormatter((val, data) => JsonConvert.DeserializeObject<DateTime>(WrapStringInQuotes(val.ToString()))))
                .Field(new Field("DaysAbsent"))
                .Field(new Field("DurationOfAbsence"))
                .Field(new Field("TotalChronicHealthRecordsInSchoolYear"))
                .Field(new Field("SbmsTotalDaysSuspendedInSchoolYear"))
                .Where("Campus", campus)
                .Process(formData)
                .Data();
    
            return Json(response, JsonRequestBehavior.AllowGet);
        }
    }
    
  • allanallan Posts: 61,734Questions: 1Answers: 10,110 Site admin

    How to do this sort of depends on how far reaching across the system you want your tests to cover. I would consider these system tests, since you are testing Editor's input / output as a black box, so there are three "levels" where I could see testing being done:

    1. Complete end-to-end from the browser. Create a sample application with a database and C# backend, a web-page interface and use Selenium or similar to automate testing. That way you can verify that the browser shows what is expected, regardless of what the backend is doing (thus testing the back end implicitly).
    2. C# only testing. Construct a DtRequest object that Editor would be sending to the server for a row create, edit, delete and then test the response (DtResponse) to make sure it is what you expect. That removes the complexity of the browser / Selenium part, but it means that you are cutting out the testing of the client-side components.
    3. C# to database testing. Again construct the DtRequest, but rather than checking DtResponse (or perhaps in addition to it), check the database directly that what was expected was done (e.g. inserting a row with given data). Likewise, then modify the data in the database directly and read it back using Editor to make sure it is what is expected.

    Allan

  • rdmrdm Posts: 192Questions: 54Answers: 4

    Thanks. #2 looks like a good place to start. My challenge is learning more how HttpContext.Request.Form works so that I can manually set values needed to test DtResponse. It looks like stating something like var column1 = "something" won't quite work as HttpContext.Request.Form is a NameValue collection.

  • allanallan Posts: 61,734Questions: 1Answers: 10,110 Site admin

    Yes - its a NameValue collection with structured data in the names. The DtRequest class deserialises it into a nested structure.

    For example - it might look something like:

    "action" = "edit";
    "data[row_5][first_name]" = "Airi";
    "data[row_5][last_name]" = "Satou";
    

    The data structure Editor uses is documented here.

    Regards,
    Allan

This discussion has been closed.