I have an angular service that is used as a factory function to instatiate many object instances of the type Engine like this:
angular.module('parts.engine', []).factory('Engine', function() {
var Engine = function( settings ) {
this.hp = settings.engine.hp;
this.miles = 0;
};
Engine.prototype.setMiles = function( miles ) { this.miles = miles; }
return Engine;
});
Say I have another angular service, that is also used to create instances of an object like this:
angular.module('car', ['parts.engine']).factory('carCreator', function( Engine ) {
var carCreator = function( settings ) {
var engine = new Engine( settings );
engine.setMiles( settings.engine.miles )
return {
brand: settings.brand;
engine: engine;
}
};
return carCreator;
});
So I now instatiate a new instance of a car object like this:
angular.module('carApp', ['car']).controller('AppCtrl', function( carCreator ) {
var settings = {
brand: 'Ford',
engine: {
hp: 125,
miles: 12000
}
};
var newCar = carCreator(settings);
});
Does anyone have an idea how to test the initialization logic:
var engine = new Engine( settings );
engine.setMiles( settings.engine.miles )
in the carCreator factory? I know I can instantiate an object with the carCreator class and check, if the returned objects engine.miles property is set to the correct value. But I have cases, where checking for this will not be as easy, because the initalization logic and the values returned are much more complex. What I would like to do is test the businesslogic of setMiles in the Engine class, and just setup a SpyOn on Engine and engine.setMiles when testing the carCreator class, but how do I do that, when engine is created in the closure?
You can use $provide to provide a mock version of the Engine service to carCreator. You'll then be able to spy on it and assert that the correct steps have been taken.
One of the trickier bits of this is that you're testing a constructor function, which jasmine mocks doesn't play so nicely with, you need to return a mockInstance and spy on that instance.
var mockEngine, mockEngineInstance, carCreatorService;
beforeEach(function () {
module('car', function ($provide) {
// Define a mock instance
mockEngineInstance = {
setMiles: function () {}
};
// Mock the engine service to give a mocked instance
mockEngine = function () {
return mockEngineInstance;
};
$provide.value('Engine', mockEngine);
});
inject(function (carCreator) {
carCreatorService = carCreator;
});
});
it('should test that miles are set when a car is created',
function () {
// Arrange.
spyOn(mockEngineInstance, 'setMiles');
// Act.
carCreatorService({
engine: {
miles: 100
}
});
// Assert.
expect(mockEngineInstance.setMiles).toHaveBeenCalledWith(100);
}
);
Related
I use the System.IO.Abstractions.TestingHelpers to mock FileSystem. In my class, I inject IFileSystem and use the instance to call _fileSystem.File.Exists and _fileSystem.File.Delete. In my test class, I would like to verify that the "Delete" method was called. It's easy by mocking only the IFile, but since I already mocked the FileSystem, I don't want to have to mock the Directory, Path and File on top of it. Is it possible to call something like _fileRepository.FileMock.Verify(x => x.Delete(It.IsAny<string>()))...?
public class Downloader : IDownloader
{
public Downloader(HttpClient httpClient, IFileSystem fileSystem)
{
HttpClient = httpClient;
FileSystem = fileSystem;
}
public async Task DownloadConfigFileAsync(string updatedConfigBaseFolderPath, string configurationFileUrl, string personalAccessToken)
{
var newFilePath = FileSystem.Path.Combine(updatedConfigBaseFolderPath, "subfolder1", "myNewFile.txt");
if (FileSystem.File.Exists(newFilePath))
{
FileSystem.File.Delete(newFilePath);
}
// rest of implementation ommited for demo purpose
}
}
And my test is like :
[Fact]
public async void Given_MissingPathParts_ShouldThrow()
{
var handlerMock = GetMessageHandlerMock();
var mockFileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
{
{ #"c:\Test\", new MockDirectoryData() },
{ #"c:\Test\subfolder1\myNewFile.txt", new MockFileData(string.Empty) }
});
var httpClient = new HttpClient(handlerMock.Object);
var sut = new Downloader(httpClient, mockFileSystem);
await sut.DownloadConfigFileAsync(BasePath, "http://fakeurl.com?path=%2Fconfiguration%2Flocal%2FTestFile.txt", _fixture.Create<string>());
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1),
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get),
ItExpr.IsAny<CancellationToken>());
mockFileSystem.File.Exists(FilePath).Should().BeTrue();
// Add assertion that the File.Delete has been called
}
The test freamwork has extensive unit tests itself.
Looking into ...tests\TestableIO.System.IO.Abstractions.TestingHelpers.Tests\MockFileDeleteTests.cs, it just counts the files, which I don't find overly satisying. If there would be a direct way, the creator would have used it.
var fileCount1 = fileSystem.Directory.GetFiles(directory, "*").Length;
fileSystem.File.Delete(path);
var fileCount2 = fileSystem.Directory.GetFiles(directory, "*").Length;
Assert.AreEqual(1, fileCount1, "File should have existed");
Assert.AreEqual(0, fileCount2, "File should have been deleted");
What is the/one correct way to test this piece of JavaScript code using, e.g, Mocha/Sinon:
var App = function(endPoint, successCallback) {
var channel = new WebSocket(endPoint);
channel.onopen = function(ev) {
successCallback();
};
};
I'm thinking of something like this:
describe('App', function() {
it('test should create instance and call success', function(done) {
var app = new App('ws://foo.bar:123/', done);
var stub = sinon.stub(app, 'channel');
stub.yield('onopen');
});
});
Apparently, that does not work as channel is not accessible from outside the constructor. How would you test this?
Why not create a factory for Websocket such as:
var myApp = {
createWebsocket: function () {
return new Websocket;
}
};
This would make spying on the myApp.createWebsocket return value channel very easy:
sinon.spy(myApp, 'createWebsocket);
var channel = myApp.createWebsocket.firstCall.returnValue;
var stub = sinon.stub(channel, 'onopen');
stub.yield('onopen');
// Clean up
myApp.createWebsocket.restore();
Does Jasmine's spyOn() method allow the spied on function to be executed, or does it, kind of - intercept the invocation when the spied method is (about to get) invoked, and returns true.
PS: Could anyone point me to the explanation of spyOn()'s inner workings?
Spy :
A spy can pretend to be a function or an object that you can use while writing unit test code to examine behavior of functions/objects
var Person = function() {};
Dictionary.prototype.FirstName = function() {
return "My FirstName";
};
Dictionary.prototype.LastName = function() {
return "My LastName";
};
Person.prototype.MyName = function() {
return FirstName() + " " + LastName();
};
Person.prototype.MyLocation = function() {
Return ”some location”;
};
describe("Person", function() {
it('uses First Name and Last Name for MyName', function() {
var person = new Person;
spyOn(person , "FirstName");
spyOn(person, "LastName");
person.MyName();
expect(person.FirstName).toHaveBeenCalled();
expect(person.LastName).toHaveBeenCalled();
});
});
Through SpyOn you can know whether some function has been / has not been called
expect(person. MyLocation).not.toHaveBeenCalled();
You can ensure that a spy always returns a given value and test it
spyOn(person, " MyName ").andReturn("My FirstNameMy LasttName ");
var result = person.MyName();
expect(result).toEqual("My FirstName My LasttName ");
Spies can call through to a fake function
it("can call a fake function", function() {
var fakeFun = function() {
alert("I am a spy!”);
return "hello";
};
var person = new person();
spyOn(person, "MyName").andCallFake(fakeFun);
person. MyName (); // alert
})
You can even create a NEW spy function or object and make use of it
it("can have a spy function", function() {
var person = new Person();
person.StreetAddress = jasmine.createSpy("Some Address");
person. StreetAddress ();
expect(person. StreetAddress).toHaveBeenCalled();
});
It just creates a mock(spy) object and injects it to your tested code.
It has three main purposes:
Testing your code if it calls spy: toHaveBeenCalled
Testing your code if it calls with appropriate parameters: toHaveBeenCalledWith
Testing your code for different return values from spy: and.callThrough
This may be a duplicate but I have looked at a lot of other questions here and they usually miss what I am looking for in some way. They mostly talk about a service they created themselves. That I can do and have done. I am trying to override what angular is injecting with my mock. I thought it would be the same but for some reason when I step through the code it is always the angular $cookieStore and not my mock.
I have very limited experience with jasmine and angularjs. I come from a C# background. I usually write unit tests moq (mocking framework for C#). I am use to seeing something like this
[TestClass]
public PageControllerTests
{
private Mock<ICookieStore> mockCookieStore;
private PageController controller;
[TestInitialize]
public void SetUp()
{
mockCookieStore = new Mock<ICookieStore>();
controller = new PageController(mockCookieStore.Object);
}
[TestMethod]
public void GetsCarsFromCookieStore()
{
// Arrange
mockCookieStore.Setup(cs => cs.Get("cars"))
.Return(0);
// Act
controller.SomeMethod();
// Assert
mockCookieStore.VerifyAll();
}
}
I want mock the $cookieStore service which I use in one of my controllers.
app.controller('PageController', ['$scope', '$cookieStore', function($scope, $cookieStore) {
$scope.cars = $cookieStore.get('cars');
if($scope.cars == 0) {
// Do other logic here
.
}
$scope.foo = function() {
.
.
}
}]);
I want to make sure that the $cookieStore.get method is invoked with a 'garage' argument. I also want to be able to control what it gives back. I want it to give back 0 and then my controller must do some other logic.
Here is my test.
describe('Controller: PageController', function () {
var controller,
scope,
cookieStoreSpy;
beforeEach(function () {
cookieStoreSpy = jasmine.createSpyObj('CookieStore', ['get']);
cookieStoreSpy.get.andReturn(function(key) {
switch (key) {
case 'cars':
return 0;
case 'bikes':
return 1;
case 'garage':
return { cars: 0, bikes: 1 };
}
});
module(function($provide) {
$provide.value('$cookieStore', cookieStoreSpy);
});
module('App');
});
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller;
}));
it('Gets car from cookie', function () {
controller('PageController', { $scope: scope });
expect(cookieStoreSpy.get).toHaveBeenCalledWith('cars');
});
});
This is a solution for the discussion we had in my previous answer.
In my controller I'm using $location.path and $location.search. So to overwrite the $location with my mock I did:
locationMock = jasmine.createSpyObj('location', ['path', 'search']);
locationMock.location = "";
locationMock.path.andCallFake(function(path) {
console.log("### Using location set");
if (typeof path != "undefined") {
console.log("### Setting location: " + path);
this.location = path;
}
return this.location;
});
locationMock.search.andCallFake(function(query) {
console.log("### Using location search mock");
if (typeof query != "undefined") {
console.log("### Setting search location: " + JSON.stringify(query));
this.location = JSON.stringify(query);
}
return this.location;
});
module(function($provide) {
$provide.value('$location', locationMock);
});
I didn't have to inject anything in the $controller. It just worked. Look at the logs:
LOG: '### Using location set'
LOG: '### Setting location: /test'
LOG: '### Using location search mock'
LOG: '### Setting search location: {"limit":"50","q":"ani","tags":[1,2],"category_id":5}'
If you want to check the arguments, spy on the method
// declare the cookieStoreMock globally
var cookieStoreMock;
beforeEach(function() {
cookieStoreMock = {};
cookieStoreMock.get = jasmine.createSpy("cookieStore.get() spy").andCallFake(function(key) {
switch (key) {
case 'cars':
return 0;
case 'bikes':
return 1;
case 'garage':
return {
cars: 0,
bikes: 1
};
}
});
module(function($provide) {
$provide.value('cookieStore', cookieStoreMock);
});
});
And then to test the argument do
expect(searchServiceMock.search).toHaveBeenCalledWith('cars');
Here is an example https://github.com/lucassus/angular-seed/blob/81d820d06e1d00d3bae34b456c0655baa79e51f2/test/unit/controllers/products/index_ctrl_spec.coffee#L3 it's coffeescript code with mocha + sinon.js but the idea is the same.
Basically with the following code snippet you could load a module and substitute its services:
beforeEach(module("myModule", function($provide) {
var stub = xxx; //... create a stub here
$provide.value("myService", stub);
}));
Later in the spec you could inject this stubbed service and do assertions:
it("does something magical", inject(function(myService) {
subject.foo();
expect(myService).toHaveBeenCalledWith("bar");
}));
More details and tips about mocking and testing you could find in this excellent blog post: http://www.yearofmoo.com/2013/09/advanced-testing-and-debugging-in-angularjs.html
Why mock cookieStore when you may use it directly without modification? The code below is a partial unit test for a controller which uses $cookieStore to put and get cookies. If your controller has a method known as "setACookie" that uses $cookieStore.put('cookieName', cookieValue) ... then the test should be able to read the value that was set.
describe('My controller', function() {
var $cookieStore;
describe('MySpecificController', function() {
beforeEach(inject(function(_$httpBackend_, $controller, _$cookieStore_) {
$cookieStore = _$cookieStore_;
// [...] unrelated to cookieStore
}));
it('should be able to reference cookies now', function () {
scope.setACookie();
expect($cookieStore.get('myCookieName')).toBe('setToSomething');
});
});
If you look at the example at this link:
http://www.atlanticbt.com/blog/asp-net-mvc-using-ajax-json-and-partialviews/
How would one write a unit test for the JsonAdd method? I have a similar situation in my own code, but the RenderPartialViewToString errors when calling:
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView
I've tried different ways of trying to stub that call to no avail. Any help appreciated. Thanks.
I had a lot of trouble to make unit test working with RenderPartialViewToString.
I succeeded by doing 2 things.
I had to mock the view engine and the controller context.
Here the code :
public ViewEngineResult SetupViewContent(string viewName, string viewHtmlContent)
{
var mockedViewEngine = new Mock<IViewEngine>();
var resultView = new Mock<IView>();
resultView.Setup(x => x.Render(It.IsAny<ViewContext>(), It.IsAny<TextWriter>()))
.Callback<ViewContext, TextWriter>((v, t) =>
{
t.Write(viewHtmlContent);
});
var viewEngineResult = new ViewEngineResult(resultView.Object, mockedViewEngine.Object);
mockedViewEngine.Setup(x => x.FindPartialView(It.IsAny<ControllerContext>(), viewName, It.IsAny<bool>()))
.Returns<ControllerContext, string, bool>((controller, view, useCache) =>
{
return viewEngineResult;
});
mockedViewEngine.Setup(x => x.FindView(It.IsAny<ControllerContext>(), viewName, It.IsAny<string>(), It.IsAny<bool>()))
.Returns<ControllerContext, string, string, bool>((controller, view, masterName, useCache) =>
{
return viewEngineResult;
});
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(mockedViewEngine.Object);
return viewEngineResult;
}
public void SetContext(ref PointCollecteLivraisonController controller)
{
SetupViewContent("MyViewName", "TheViewContent");
var httpContextBase = new Mock<HttpContextBase>();
var httpRequestBase = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var routes = new RouteCollection();
RouteConfigurator.RegisterRoutes(routes);
var routeData = new RouteData();
routeData.Values.Add("controller", "PointCollecteLivraison");
routeData.Values.Add("action", "RechercheJson");
httpContextBase.Setup(x => x.Response).Returns(response.Object);
httpContextBase.Setup(x => x.Request).Returns(httpRequestBase.Object);
httpContextBase.Setup(x => x.Session).Returns(session.Object);
session.Setup(x => x["somesessionkey"]).Returns("value");
httpRequestBase.Setup(x => x.Form).Returns(new NameValueCollection());
controller.ControllerContext = new ControllerContext(httpContextBase.Object, routeData, controller);
controller.Url = new UrlHelper(new RequestContext(controller.HttpContext, routeData), routes);
}
And that is the way i use it all :
PointCollecteLivraisonController controller = new PointCollecteLivraisonController();
SetContext(ref controller);
Here are my sources :
View engine mocking : http://thoai-nguyen.blogspot.fr/2011/04/test-mock-mvc-view-engine.html
Controller context mocking : ASP.NET MVC - Unit testing RenderPartialViewToString() with Moq framework?
Hope this help.
Since ViewEninges is a static class, you can't mock it with RhinoMocks. I think your best bet is to create a "partial view renderer" interface. An interface is mockable so you'll be able to stub out the complexity of rendering the view. Here's some quick pseudo-code thrown together.
First, define the partial view renderer interface:
public interface IRenderPartialView
{
string Render(string viewName, object model);
}
Then, change your base class' RenderPartialViewToString to be the implementation of IRenderPartialView.Render:
public abstract class BaseController : Controller, IRenderPartialView
{
...
public string Render(string viewName, object model)
{
// same code as RenderPartialViewToString
}
}
Now we need to change your controller constructors so we can inject an IRenderPartialView during testing -- but use the base class one during production. We can accomplish this by using a pair of constructors:
public class YourController : BaseController
{
private IRenderPartialView partialRenderer;
public YourController()
{
SetRenderer(this);
}
public YourController(IRenderPartialView partialRenderer)
{
SetRenderer(partialRenderer);
}
private void SetRenderer(IRenderPartialView partialRenderer)
{
this.partialRenderer = this;
}
}
Now, JsonAdd can call the partial view renderer:
public JsonResult JsonAdd(AddPersonViewModel AddPersonModel)
{
...
return Json(new
{
Success = true,
Message = "The person has been added!",
PartialViewHtml = partialRenderer.Render("PersonList", new PersonListViewModel {PersonList = _personList})
});
}
So, during testing, you'll mock out an IRenderPartialView and send that to the constructor that accepts an IRenderPartialView. During production, when ASP.NET MVC calls your default constructor, it will use the controller as the renderer (which has the implementation of IRenderPartialView.Render inside the base class).