2

I am writing automated test cases in Visual Studio 2017 test frame work. I am testing the ‘create user’ part of a web API. A user needs to be created with a unique email address. Once a user is created they cannot be deleted.

Is there way to write automated (run on check-in) test cases, that need unique input data for each test run?

In the example below Username, must be unique on each call. Username can be up to 30 characters long.

ProcessStudent(
    string StudentAuthId, 
    string FirstName, 
    string MiddleName, 
    string LastName, 
    string UserName, 
    string UserPassword
);

(My thoughts) Since there is no way (I can think of) to make this test atomic, there needs to be unique input values each time the test is run. This could be done by appending some version of a time stamp to the UserName passed in. I am not sure this is the best way of doing this so I am asking this question.

  • 5
    You should be controlling the initial database state for your tests, so it only has to be different from the other users in that initial state. – jonrsharpe Apr 19 '18 at 13:09
  • Also, if you have some behavior to test here, such as validating input formats like the email address, it would be good to isolate it so you can test it without getting the DB involved at all. – candied_orange Apr 19 '18 at 13:17
  • @jonsharpe standing up a dedicated automated testing DB is not an option for me. The code is still under development and we have one dev DB all of out local instances of the service point to. – Willis White Apr 19 '18 at 13:36
  • @CandiedOrange - part of the validation involves checking values in DB but nothing is written to DB in this call – Willis White Apr 19 '18 at 13:37
  • 2
    "The code is still under development" -- good: that means it's not too late to do this properly -- "and we have one dev DB all of out local instances of the service point to" -- so get another DB. I tend to run my tests against an in-process DB (e.g. H2, Apache Derby, etc) because they're easier to set up and tear down, but even if you have something server-specific it's still possible to run an instance on the local development machine and reset it between tests. – Jules Apr 19 '18 at 15:00
2

It is common to need unique values when testing. So, you might like to look for a library that already provides that functionality for you.

If you can't find an existing library, then you could consider rolling your own with reusability in mind:

  • You could start with generating a unique integer. The first implementation could use the timestamp as you suggested. If that sometimes causes collisions, then you might want to only initialize a counter with a timestamp and increment it on every call. If you run your tests in multiple threads or multiple processes, then you could adjust accordingly.
  • You could continue with generating a unique string. The first implementation could use the stack to include the filename and line number of the caller in the string, to which you could append the unique integer in the previous bullet. If that generic string makes it hard to troubleshoot, then you could parameterize the function to take a prefix or a suffix to make it more specific.
  • You could be even more specific with generating a unique username. If generating a unique string violates some username constraints, this function could change the string accordingly. For example, if the filename, line number and unique integer in the unique string are separated with dashes but that character is not permitted in a username, then you could either parameterize the unique string function to take a separator character or you could just substitute the dashes in the string.

By building a library, it can be easily unit tested so that other tests can use it with confidence. Also, by reusing functions, you could incrementally improve it as you encounter potential collisions. For example, if you improve the unique integer function then both the unique string and unique username in the above design will benefit; not to mention all the tests that use this library.

  • Do you know of any C# libraries that provide this functionality? – Willis White Apr 19 '18 at 18:12
  • @WillisWhite, I don't work in C# but if you can't find one, then that might be a good opportunity to introduce one to which others may contribute. – cr3 Apr 19 '18 at 18:45
2

Your issue is likely that your processing code is coupled tightly to your DB, try decoupling and things will get much easier:

class StudentProcessor
{
     Func<string, bool> CheckUsernameIsUnique;
     public StudentProcessor(Func<string, bool> checkUsernameIsUnique)
     {
         CheckUsernameIsUnique = checkUsernameIsUnique;
     }

     public ... ProcessStudent(...)
     {
         CheckUsernameIsUnique(username);
         ...
     }
}

It is now much easier to test what happens here

void Test()
{
     var processor = new StudentProcessor(x => {"username1", "username1"}.Contains(x));
     processor.ProcessStudent(...);
}

What happened here, is that your unit testing problems has shown you that your current implementation has tight coupling which makes it hard to maintain. This is not a problem with unit testing, it is its main benefit :)

0

I took the suggestion of cr3. I used the Bogus lib for C#. Using the Bogus lib and Microsoft.VisualStudio.TestTools.UnitTesting I was able to create random test data for each test that needed it. I used the word “Purge” in the random data so that it could be found and removed later. I wanted to post an example, since that is what I was looking for originally.

[TestInitialize]
    public void Initialize()
    {
        //Replace a formatted string with random numbers #, letters ?, or * random number or letter
        var faker = new Faker("en");

        StudentAuthIdRandom = new Bogus.Randomizer().Replace("???:???:???-Purge");
        FirstNameRandom = new Bogus.Randomizer().Replace("????-Purge");
        MiddleNameRandom = new Bogus.Randomizer().Replace("?????-Purge");
        LastNameRandom = new Bogus.Randomizer().Replace("??????-Purge");         
        UserNameRandom = new Bogus.Randomizer().Replace("????????-Purge");
        UserPasswordRandom = new Bogus.Randomizer().Replace("?*****-Purge");
    }

Note: I have learned from the other answers that what I am doing is integration (not unit) testing and there should be a dedicated DB for this. Having a dedicated DB for this work was not an option for me so I used data values that could easily be found and removed for the dev BD we are using.

thank you for all the responses and suggestions

-1

It sounds like you are writing integration tests that require a live Web API. If that is the case, it is perfectly fine to use the live web API for testing purposes, but you need to control the database.

Before each test, wipe the database clean. Then insert exactly the data you need for the test. Run the test. Assert. Rinse and repeat.

If are not testing the integration of your application and the API, then you need to decouple those calls from your core business logic, usually by defining an interface for the Web API object and mocking it in your other tests.

I realize you said you cannot control the database. If that is the truth, then you shouldn't be including the database as part of the tech stack under test.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.