Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Saturday, 6 February 2016

Unit testing with Karma and Jasmine for AngularJS

So you’ve started to build applications with AngularJS; or, maybe you’ve done unit testing before but haven’t used it with AngularJS; or, maybe it’s all new to you, but either way this article should help to orient you to the value of using Karma with Jasmine and offer some tips on displaying functionalities of Angular JS. Let’s get started!
My app is small it doesn’t need any tests, right?
Wrong!
A small app can have errors and it’s bad practice to have an untested app.
Besides, a small app is the perfect place to get started in using proper unit testing because there won’t be many tests and the functionality will be less complex.
First of all, what is Unit Testing?
Unit testing is very important for both the quality of the code and the well being of the developer. It allows for them to freely change their code and not just hope that it’s not a breaking change, as the test will recognize the break straight away. This will minimize the number of regressions in the code.
Unit tests are all about splitting your code into small testable modules with each module having its own functionality and level of abstraction. This is where AngularJS shines.
An added bonus is that the unit tests will act as a type of documentation because it describes the expected behavior of the functions and modules. Unit tests don’t care how your code does the task, it just cares about the results.
Okay, but when should I write unit tests?
Short answer – the start of the project is the ideal time. You will have all your tests and then can work toward writing your code with passing them in mind.
Sadly it is unrealistic to expect complete coverage with unit testing, so my suggestion is write as many as you can possibly think of at the start, and if new components appear, then they should be tested too; a more iterative process of revisiting tests should be used.
What testing framework should I use?
AngularJS can be tested in a lot of ways, by lots of testing frameworks, including but certainly not limited to mocha, jasmine, qunit, and sin on.
There are a lot of choices, but here’s the secret: your choice really doesn’t matter because there is no “best” option; they are all very similar.
If you are torn and love brilliant documentation (who doesn’t) I would choose Jasmine (used proudly by the Logentries team btw).
After you pick your testing framework, you can now choose a task runner. The most well known include Karma and Protractor (both by the Angular team). For our examples we will be using Karma.
Now that you know some basics on why you should be using unit test, and you have an Angular project ready, lets get our hands on an example.
We’ll need to install some dependencies for running unit tests with Karma and Jasmine.
To do this, you should run the following lines on the command line:
npm install karma --save-dev
npm install karma-phantomjs-launcher --save-dev
npm install karma-jasmine --save-dev
Now configure your karma.conf.js file using this guide.
How to design a test?
Scenario
  • “A calculator that adds and subtracts”
This scenario can be broken into features, which will in turn be broken into units of testable code.
The calculator has two main features, adding and subtracting numbers. These features can be split into units, such as the methods for addition and subtraction. These methods need to be tested and there may be many different cases that need to be taken into account. A possible example is below, but you may come up with some more tests.
describe('Calculator ', function() {

// necessary
it('should add two numbers correctly', function() {});
it('should subtract two numbers correctly', function() {});

// helpful but not needed
it('should add negative numbers', function() { });
it('should reject non numbers', function() { });
});

The “describe()” method defines our test suite. This takes a string with the convention that it describes the module which is being tested.
An “it()” is a proper test spec. Put what you want to be tested here, the convention is that it should read as a sentence, e.g. ‘it is expected to do this’ reads as “it is expected to do this”
How do I use it with my AngularJS app?
Here is an example of a very basic controller. It only has a scope variable and we will test if this value is correct.
 var myApp = angular.module('myApp',[]);
myApp.controller('HelloWorldController', ['$scope', function($scope) {
$scope.greeting = 'Hello World!';
}]);
Jasmine Test
describe(‘Hello World example ’, function() {

beforeEach(module(‘myApp’));

var HelloWorldController,
scope;

beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
HelloWorldController = $controller('HelloWorldController', {
$scope: scope
});
}));
it('says hello world!', function () {
expect(scope.greeting).toEqual("Hello world!”);
});

});

Run your test with:
karma start karma.conf.js

Every “beforeEach()” function is executed right before the tests are conducted. In the above case the function gets access to your angular module and then injects the controller and scope, so that it can be tested. This allows you to set up for each assertion, allowing the same clean set up for all your tests.
Some of the possible expectations include:
  • toEqual, toBe, toBeTruthy, toBeFalsy
  • toBe for negation
Jasmine has you covered if you want to ignore some of the tests temporarily. “xdescribe” will ignore a “describe” and “xit” will ignore a specific test.
Conclusion
So now you can run a basic unit test for your AngularJS app and slowly begin to build out to more complex tests. Once you’ve got your unit tests in place you have the foundations for a great application!

AngularJS–Part 7, Getting ready to test

This is a series of posts about AngularJS and our experiences with it while migrating the client of a complex enterprise application from Silverlight to HTML5/CSS/JavaScript using AngularJS as a framework. Since the migration is a very ambitious undertaken I want to try to chop the overall problem space in much smaller pieces that can be swallowed and digested much more easily by any member of my team. So far I have published the following posts in this series

Test driven development (TDD) is very important when developing a web application with a rich client since JavaScript is a dynamic language and thus there is no compiler making sure that our code behaves like expected. Tests are our only safety net. Writing tests in JavaScript and running them in a browser is not trivial. Lately the situation has improved quite a bit. Still, to get up and running for the first time is a non trivial task.

Setting up the system

Please note that I am using a laptop with Windows 8.1 as the operating system. But the following setup should also work without any modifications on Windows 7 & 8.
To run unit tests we are going to use Karma (previously named Testacular). Karma is a test runner created by the AngularJS team at Google. Similar to AngularJS itself it is open source. To write the tests we will use Jasmine which is a behavior driven JavaScript test framework or DSL.
A prerequisite for Karma is node.js. Thus please install node.js on your computer
Open a console in admin mode. Preferably use an advanced console like Console2. Use the Node package manager (NPM) to install Karma. We want to install it globally thus use this command
image
Now we need to configure Karma for use in our project
image
This command generates a configuration file (it is a JavaScript file exporting a settings object) that defines
  • which test framework to use (Jasmine in our case),
  • which files to monitor and use (this includes at least)
    • angular.js
    • angular-mocks.js (contains the definition for module and inject)
    • our own code to test
    • the files containing the tests or specifications
  • what browsers to use to run the test against (we’ll stick with Chrome)
  • The port on which the server is listening
  • whether to run the test automatically or manually when any of the monitored files changes
This is my configuration file after manually tweaking it
image
Once we have configured Karma correctly we can run it (remember Karma is a test runner) by using the following command
image
Karma will now monitor all the files we have declared in the config file and run the tests if any of those files changes.
When starting up Karma starts up an instance of the browser(s) we defined in the configuration file. The browser listens at the port also defined in the configuration file.
image
We do not need to observe the browser instance any further and can minimize (not close!) it, since it won’t show any test results. All test results will be shown in the console window where we started Karma. Since we do not yet have any files containing tests Karma will report an error that no matching files were found.
image
But we also know that we have correctly configured our system and we are now ready to write our first tests.

Writing our first test

First we copy the two files angular.js and angular-mocks.js into our application directory. Then we create a new subfolder js and add a file sample.js to this new folder. Using e.g. Sublime we define a simple controller called SampleCtrl which does nothing else than initialize a property hello on the $scope of the controller.
image
This is the controller we want to write a test against. Create a new directory test and add a file sampleSpec.js to this directory. This file will contain our tests that we write using Jasmine. Please refer to the Jasmine documentation for details about the syntax.
We want to write tests against the SampleCtrl controller, thus we write
image
This roughly corresponds to a test class in C#. We provide a description of the scenario as the first parameter of the describe function and a function containing all our test arrangements, the test act and the test assertions (AAA = arrange, act, assert) as second parameter.
To arrange or prepare our tests we can call the method beforeEach. As first we need to make sure our application module is instantiated. Thus we write
image
Where we use the module function provided by angular-mocks.js.
Next we need to configure our controller. In looking back at how the controller is coded we realize that it is a function that gets the $scope injected. To be able to test we need to replace the $scope with our own scope object which we can then later you to execute assertions against. Thus we write
image
Here we use the inject function defined in angular-mocks.js to provide us the $controller and the $rootScope service via injection. We then use the $rootScope as our scope and use the $controller service to get an instance of the SampleCtrlcontroller. The second parameter of the $controller service call is the parameters that get injected into the controller. In this case it is only one parameter – the $scope – which we provide here.
Having configured everything we are now ready to write our first assertion. For this we can use the it function provided by Jasmine.
image
I have on purpose made a little mistake and I’m expecting “Hello John” without point instead of “Hello John.” with point. I want us to see how a broken test looks like in the output of Karma. The complete code of the specification should look like this
image
When we save this file and switch to Karma we should see the following output
image
The output clearly says that one test was executed and it failed since the expected value did not correspond to the actual value. Let’s correct this in the specification and save the file again. Karma will now output this
image
and we are happy and can go have a beer Smile
Yes, I’m aware, this was not TDD what I demonstrated so far since I first wrote the code and then the test. But this post was mainly about getting our system setup correctly and ready to start TDD.

Summary

In this post I have show how to setup our system to be able to write unit tests for an AngularJS application. We are using Jasmine as the test framework and Karma as the test runner. A crucial step in the whole setup is to author the correct configuration file for Karma.

Thursday, 20 November 2014

MOCKING ENTITY FRAMEWORK 6 CONTEXT WITH RHINO MOCKS (CODE FIRST)


Standard

NOTE: You must be using Rhino.Mocks 3.6.1 or higher.
As we have decided to use Entity Framework 6 for all our projects going forward we recently had some issues mocking out the context class for unit testing the next layer up from the context. Most of my findings searching online gave me examples of how to mock the context using MOQ another mocking framework for unit testing. Ill leave the discussion of the different mocking frameworks for another post or another blogger (there is lots of information out there).
A note about the setup… We use a code first entity framework setup which is how I am going to code this example.

First we have a business object (model) that is a POCO:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthdate { get; set; }
}
Second we must create the context:
public class ExampleContext : DbContext
{
       publicExampleContext()
            : base(“ExampleConnectionStringName”)
        { }
      public virtual IDbSet<Customer> Customers { get; set; }
}
Notice that the property for Customers in the context is virtual and is an IDbSet not a DbSet. This is important for being able to stub the value of the Customers property.
We now can create a method in a class that uses the context to get some data from the database that we will unit test. We will keep this simple:
public class CustomerRepository
{
private ExampleContext Context;

public CustomerRepository(ExampleContext context)
{
Context = context;
}

public IEnumerable<Customer> GetCustomersByBirthdate(DateTime birthdate)
{
return context.Customers.Where(x => x.Birthdate == birthdate).ToList();
}
}

We will now write our unit test to test the GetCustomersByBirthdate method. Notice the class constructor takes the context as an argument instead of creating it in the constructor of the class with the new keyword. This is to allow us to pass the mock context into the class to use, it promotes loose coupling of the classes. More can be learned about this by looking up Inversion of Control or Dependency Injection. In the unit test we need to setup a mock of the context and setup the fake data to be returned from the context when asked. Here is the Setup method for the unit tests:
public class CustomerRepositoryTests
{
protectedExampleContext MockContext;
protected IQueryable<Customers> MockData;
protected IDbSet<Customers> MockSet;
        [SetUp]
public void SetUp()
        {
            MockContext = MockRepository.GenerateMock<ExampleContext>();
            MockSet = MockRepository.GenerateMock<IDbSet<Customer>IQueryable>();
            MockData = new List<Customer>
            {
new Customer { Id= 119, FirstName = “Bob”, LastName = “Johnson”, Birthdate = new DateTime(1981, 1, 1) },
new Customer { Id = 120, FirstName = “Bob”, LastName = “Johnson”, Birthdate = new DateTime(1981, 1, 1) },
new Customer { Id = 118, FirstName = “Bob”, LastName = “Johnson”, Birthdate = new DateTime(1971, 10, 10) }
            }.AsQueryable();
            // These were hard to find
            MockSet.Stub(m => m.Provider).Return(MockData.Provider);
            MockSet.Stub(m => m.Expression).Return(MockData.Expression);
            MockSet.Stub(m => m.ElementType).Return(MockData.ElementType);
            MockSet.Stub(m => m.GetEnumerator()).Return(MockData.GetEnumerator());
            MockContext.Stub(x => x.Customers).PropertyBehavior();
            MockContext.Customers = MockSet;
        }
Notice that the hard to find part is stubbing items on the IQueryable interface and notice that when we create the mock for the IDbSet it is actually a multi-mock where more than one interface becomes part of the mock object. And here is the actual test using the mock context and calling the method.
        [Test]
      public void GetFactsByLocationId_SingleResult_ReturnsCorrectResult()
        {
         const int testId = 120;
         var testRepository = new CustomerRepository(MockContext);
            IEnumerable<Customer> result =testRepository.GetCustomersByBirthdate(testId);
            Assert.That(result.Count, Is.EqualTo(1));
        }
That is how you mock out the context. The part I struggled with was the setup method and making sure the DbSet properties are virtual and IDbSet instead. I hope this was helpful as I spend a bit of time trying to figure this out.

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...