I have create a web API am have developed some unit tests for it. In one of my controllers I pass in a variable of a Id which runs a stored procedure and returns a list of data with the matching data. Here is one of my unit tests:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web.Http.Results;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WebApi.Controllers;
using WebApi.Models;
namespace UnitTestProject1
{
[TestClass]
public class ProductContollerTests
{
// Unit Test Description: Tests to check the number of returned items with the BrandId = 1 matches the number of Products In testProducts
[TestMethod]
public void GetProducts_ShouldRetrunCorrectProducts()
{
var testProducts = GetTestProducts();
var controller = new ProductsController(testProducts);
var result = controller.GetProducts(1) as List<Product>;
Assert.AreEqual(testProducts.Count, result.Count);
}
private List<Product> GetTestProducts()
{
var testProducts = new List<Product>
{
new Product { BrandId = 1, ProductId = 1, ProductName = "Home" },
new Product { BrandId = 1, ProductId = 2, ProductName = "Motor" },
new Product { BrandId = 1, ProductId = 3, ProductName = "Travel" },
new Product { BrandId = 1, ProductId = 4, ProductName = "Van" },
new Product { BrandId = 1, ProductId = 5, ProductName = "Commercial" }
};
return testProducts;
}
}
}
Current this is passing because I am count the data that is returned and compared to see if it is equal to the data in the list testproducts. Currently this is a unit test for when the brand id is equal to 1. But what happens if I wanted to test for when brand id is equal to 2.
I can add a new product to the list testProducts but this then cause the first test to fails as I am counting the elements in list testProducts so when i add another product e.g.
new Product { BrandId = 2, ProductId = 6, ProductName = "Creditor" }
There are more values in testProducts than the data being returned from my API. Is there a way in which i could add it so that i only count the elements which have a brandId of one so that i could add other tests or will i have to always create a new testproducts List when testing something different.
you are telling this controller to work with a list of five products and then when you call the get method with an ID of 1, which you are not even using by the way, you verify that you get 5 products back.
It's a pointless test, you're not testing anything. I am not even mentioning that you're instantiating a web api controller with a list of items for some strange reason.
Instead, here's what I would do:
I would create a list of items, for real, in an actual database, then call the get brand endpoint and verify that I get back what I should. This would be an integration test which tells you that multiple endpoints are working.
Unit testing is about functionality, changing some data somehow, following a clearly definable method, with clear inputs and outputs.
I would also move away from this idea of instantiating controllers to call methods inside them, instead create proper layers and test those. This is like building a modern API in a webforms way.
Related
We have Navision Dynamics 2017 which has Sales Orders exposed as a SOAP web service. Technically, I am supposed to be able to create sales orders via this web service.
We also have another system built in C# .NET that has staff sales orders that need to go into Navision. This ordering system has all the information like customer, item, quantity, price etc to be able to create a valid order in Navision.
Can someone tell me how I can call the service and create a sales header and Line from the staff sales orders system into Navision..
Preferably a walk through tutorial would be ideal. I've searched and can't seem to find one that I can follow
The classic 'goto' for NAV services was always the following blog post, albeit it's an example for PHP. Take into account changes are required in NAV to be able to interact with the service (Hint: Activate NTLM):
https://blogs.msdn.microsoft.com/freddyk/2010/01/19/connecting-to-nav-web-services-from-php/
There is now an updated version by the same autor, complementing the original post:
https://blogs.msdn.microsoft.com/freddyk/2016/11/06/connecting-to-nav-web-services-from-php-take-2/
Example for C#:
https://blogs.msdn.microsoft.com/freddyk/2010/01/19/connecting-to-nav-web-services-from-c-using-web-reference/
Example for completing a Sales Order:
https://blogs.msdn.microsoft.com/freddyk/2009/11/17/extending-page-web-services-and-creating-a-sales-order-again/
I googled it for you. Is for Nav 2013, but it is all the same in 2017.
https://community.dynamics.com/nav/b/ishwarsblogspot/archive/2016/09/26/register-and-consume-codeunit-as-a-web-service-in-nav-2013-r2
Here's what I did in the rest API that I developed with .NET Core 5, I created 2 pages (one for sales header and one for sales lines) and one code-unit for invoking calculate discount action on NAV.
[Route("order")]
[HttpPost]
private async Task<dynamic> createOrder(orderDTO request)
{
var systemService = this.OrderServiceProvider.GetProxy();
List<OrderServiceReference.Sales_Quote_Line> lineList = new List<OrderServiceReference.Sales_Quote_Line>();
foreach (OrderLine orderLine in request.Sales_Quote_Line)
{
Sales_Quote_Line line = new Sales_Quote_Line()
{
Type = OrderServiceReference.Type.Item,
TypeSpecified = true,
No = orderLine.No,
Quantity = orderLine.Quantity,
QuantitySpecified = true
};
lineList.Add(line);
}
var task = await systemService.CreateAsync(new OrderServiceReference.Create()
{
Dis_SQ = new Dis_SQ()
{
Salesperson_Code = request.Salesperson_Code,
Sell_to_Customer_No = request.Sell_to_Customer_No,
Order_Date = new DateTime(),
SalesLines = lineList.ToArray()
}
});
var salesQuotes = task.Dis_SQ;
// var systemService2 = new DiscountServiceReference.Dis_Discount_Cal_PortClient();
var calculateSystemWebService = this.CalculateDiscountServiceProvider.GetProxy();
await calculateSystemWebService.CalcOffersSHAsync(new CalcOffersSH()
{
Body = new CalcOffersSHBody()
{
pDocNo = salesQuotes.No,
pDocType = 0
}
});
// get sales lines
var systemService1 = this.OrderLinesServiceProvider.GetProxy();
var task1 = await systemService1.ReadMultipleAsync(new SalesOrderServiceReference.ReadMultiple()
{
filter = new HHT_SO_Filter[]
{
new HHT_SO_Filter()
{
Criteria = salesQuotes.No,
Field = HHT_SO_Fields.Document_No
}
},
bookmarkKey = "",
setSize = 200
});
return new
{
sales_header = task.Dis_SQ,
sales_line = task1.ReadMultiple_Result1
};
}
If you need any more help comment below.
I've succeeded to successfully construct a REST API using APEX language defined with an annotation: #RestResource.
I also wrote a matching Unit test procedure with #isTest annotation. The execution of the REST API triggered by a HTTP GET with two input parameters works well, while the Unit Test execution, returns a "null" value list resulting from the SOQL query shown below:
String mycase = inputs_case_number; // for ex. '00001026'
sObject[] sl2 = [SELECT Id, CaseNumber FROM Case WHERE CaseNumber = :mycase LIMIT 1];
The query returns:
VARIABLE_ASSIGNMENT [22]|sl2|[]|0x1ffefea6
I've also tried to execute it with a RunAs() method (see code below), using a dynamically created Salesforce test user, not anonymous, connected to a more powerful profile, but still receiving a "null" answer at the SOQL query. The new profile defines "View All" permission for Cases. Other SOQL queries to objects like: "User" and "UserRecordAccess" with very similar construction are working fine, both for REST APEX and Test APEX.
Is there a way to configure an access permission for Unit test (#isTest) to read the Case object and a few fields like: Id and CaseNumber. Is this error related to the "Tooling API" function and how can we fix this issue in the test procedure?
Code attachment: Unit Test Code
#isTest
private class MyRestResource1Test {
static testMethod void MyRestRequest() {
// generate temporary test user object and assign to running process
String uniqueUserName = 'standarduser' + DateTime.now().getTime() + '#testorg.com';
Profile p = [SELECT Id FROM Profile WHERE Name='StandardTestUser'];
User pu = new User(Alias='standt',Email='standarduser#testorg.com',LastName='testing',EmailEncodingKey='UTF-8',LanguageLocaleKey='en_US',LocaleSidKey='en_US',ProfileId=p.Id,TimeZoneSidKey='America/New_York',UserName=uniqueUserName);
System.RunAs(pu) {
RestRequest req = new RestRequest();
RestResponse res = new RestResponse();
req.requestURI = '/services/apexrest/sfcheckap/';
req.addParameter('useremail','testuserid#red.com');
req.addParameter('casenumber','00001026');
req.httpMethod = 'GET';
RestContext.request = req;
RestContext.response = res;
System.debug('Current User assigned is: ' + UserInfo.getUserName());
System.debug('Current Profile assigned is: ' + UserInfo.getProfileId());
Test.startTest();
Map<String, Boolean> resultMap = MyRestResource1.doGet();
Test.stopTest();
Boolean debugflag = resultMap.get('accessPermission');
String debugflagstr = String.valueOf(debugflag);
System.assert(debugflagstr.contains('true'));
}
}
}
Found a solution path by using: #isTest(SeeAllData=true)
See article: "Using the isTest(SeeAllData=true) Annotation"
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_seealldata_using.htm
I am using using Effort (for EF4) to do some unit tests.
var ctx= Effort.ObjectContextFactory.CreateTransient<TheContext>(Shared.Connection);
ctx.companies.AddObject(new company() { ID = 100, name = "Agent", is_agent = true });
ctx.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave);
The ID column in the company is an identity field. After executing the above query, it turns out the ID value is 1 instead of 100. Is there any way to control Identity_insert while using Effort
This is just to add the solution on this thread. #MrBlueSky add the archieved links of the solution on the comments above. But, DbConfiguration which is used on the link, does not exists anymore on the latest Effort. The solution still exists on Effort.EF6 [version="1.3.0"]. And you can use the method SetIdentityFields on EffortConnection.DbManager to turn on or off the identity fields
if (Effort.DbConnectionFactory.CreateTransient() is EffortConnection connection)
{
connection.Open();
connection.DbManager.SetIdentityFields(false);
connection.DbManager.ClearMigrationHistory();
connection.Close();
}
Turning on
// Add data with explicitly set id
Person initPerson = new Person { Id = 5, FirstName = "John", LastName = "Doe" };
dataInitContext.People.Add(initPerson);
dataInitContext.SaveChanges();
Assert.AreEqual(5, initPerson.Id);
// Enable identity field
connection.Open();
connection.DbManager.SetIdentityFields(true);
connection.Close();
I am trying to be a good little programmer and set up Unit tests for my Grails 2.2.3 app. The unit tests that use GORM's injected .save() method are apparently not persisting to the mock test DB. For an example, here is what one test consists of:
#TestFor(TermService)
#Mock(Term)
class TermServiceTests {
void testTermCount() {
def t = new Term(code: "201310").save(validate: false, flush: true, failOnError: true)
println "Printing Term: " + t.toString()
assert 1 == Term.count() // FAILS
assert service.isMainTerm(t) // FAILS
}
}
I did a println that ends up printing Printing Term: null, meaning the Term did not save and return a Term instance. The first assertion is false with Term.count() returning 0.
Does anyone know why this might be? I have a mock Term and TermService (via the TestFor annotation, I believe), so I'm not quite sure why this wouldn't work. Thanks!
Edit: Here is my Term class.
class Term {
Integer id
String code
String description
Date startDate
Date endDate
static mapping = {
// Legacy database mapping
}
static constraints = {
id blank: false
code maxSize: 6
description maxSize: 30
startDate()
endDate()
}
}
Looks like id generator is assigned since you have mentioned about using legacy database. Plus id is not bindable by default in domain class (map construct won't work for id). So, I think you have to end up using like below:
def t = new Term(code: "201310")
t.id = 1
t.save(...)
I have been using NBuilder for a while in unit tests to simulate in-memory data and it's awesome, then I wanted to use it to test my NHibernate mappings, I thought it was going to be transparent but I can not figure out what I am doing wrong =( it is simply not working
I am planing to test heavily my NHibernate mapping but since I have too many entities I do not want to populate data manually, that's the main reason I want to use NBuilder
just as a quick reference:
autoConfig.Override<Planet>(x =>
{
x.References(y => y.Sun).Cascade.SaveUpdate().Column("Star_id");
});
autoConfig.Override<Star>(y =>
{
y.HasMany(x => x.Planets).Inverse().Cascade.AllDeleteOrphan();
});
(If you need I can provide information about the entities and the mappings but I think they are correct since i am able to save my entities when the data is populated manually)
Manually:
using (var session = factory.OpenSession())
using (var tran = session.BeginTransaction())
{
var star = new Star { Class = StarTypes.B, Color = SurfaceColor.Red, Mass = 323.43, Name = "fu..nny star" };
star.Planets = new List<Planet>
{
new Planet { IsHabitable = true, Name = "my pla", Sun = star }
};
session.Save(star);
tran.Commit();
}
The above code actually works saving both entities to the database correctly meaning that my mappings are correct but now I want to use NBuilder to auto populate testing data like this:
var star = Builder<Star>.CreateNew().Build();
star.Planets = Builder<Planet>.CreateListOfSize(10).All().With(x => x.Sun, star).Build();
session.Save(star);
tran.Commit();
Inspecting the generated entities while debugging look correct to me, I can navigate through them without problems, but then when I want to commit the transaction I get the following error:
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [CH9_NHibernateLinqToNHibernate.Domain.Planet#00000000-0000-0000-0000-000000000001]
Any thoughts?
I found the problem, basically NBuilder was assigning a value to my Id and NHibernate was considering it 'persisted', and it was trying to update the record instead of create a new one (the error message was not helping me though...):
var star = Builder<Star>.CreateNew().Build();
star.Planets = Builder<Planet>.CreateListOfSize(10).All().With(x => x.Sun, star).With(x => x.Id, Guid.Empty).Build();