Unit Test Acumatica Pxgraph

Unit test Acumatica PXGraph

Hello everybody,

today I want to make another description of how to cover with Unit Tests Graphs in Acumatica. 

First of all I want to say that Acumatica controllers or as Acumatica names them graphs do not have any way to inject any dependency from interface. In such case it can be useful idea to use Microsoft Fakes library. 

So in order to unit test Acumatica code you will have a need to make mix of Acumatica code with Microsoft shims. Imagine that you have following graph declaration:

public class PAllocs : PXGraph<PaymentsAllocation>

and somewhere in your graph you have method like this:

        [PXButton(Tooltip = "Saves values from grid")]
        [PXUIField(DisplayName = "Save")]
        public virtual IEnumerable Save1(PXAdapter adapter)
        {
            var inputtedPoOrders = this.VendorOrders.Select()
            .ToList().Where(a => a.GetItem<POOrder>().GetExtension<POOrderExt>().AllocatedAmount > 0
            ||
                                    a.GetItem<POOrder>().GetExtension<POOrderExt>().AllocatedAmount < 0
            ).ToList();
 
            string currentDif = this.ReportItemFilter.Current.DifAmt.ToString();
 
            var apPayment = this.ReleasedPayments.Current;
            
            if (apPayment.CuryOrigDocAmt != ReportItemFilter.Current.TargetAmt)
            {
                VendorFilter.Ask("""Choose another item or modify po ords in bottom grid",
                    "Please choose another check or modify Amounts in bottom grid",
                    MessageButtons.OK, MessageIcon.Error);
                return adapter.Get();
            }
 
 
            if (inputtedPoOrders.Count == 0 || currentDif != "0.0000")
            {
                var vfRow = this.VendorFilter.Current;
                this.VendorFilter.Ask(vfRow, "Input at least one row""You should enter at least one Allocation payment, and Diff should be 0.0000",
                    MessageButtons.OK, MessageIcon.Error);
                return adapter.Get();
            }
        }

how to unit test it with Microsoft fakes?

Preparation

As I already mentioned in one of my articles, you'll need to prepare your test solution for creation of needed graphs. First step will be to make app.config in your test solution.

Below goes app.config that you'll need to add to your solution with tests. 

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <sectionGroup name="system.web" type="System.Web.Configuration.SystemWebSectionGroup, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
      <section name="pxaccess" type="PX.Data.PXAccessSection, PX.Data" />
      <section name="pxdatabase" type="PX.Data.PXDatabaseSection, PX.Data" />
      <section name="pxtrace" type="PX.Data.PXTraceSection, PX.Data" />
      <section name="pxtranslate" type="PX.Data.PXTranslationSection, PX.Data" />
      <section name="basicAuth" type="PX.Export.Authentication.BasicAuthenticationSection, PX.Export" />
      <section name="formsAuth" type="PX.Export.Authentication.FormsAuthenticationSection, PX.Export" />
      <section name="multiAuth" type="PX.Export.Authentication.AuthenticationManagerSection, PX.Export" />
      <section name="externalAuth" type="PX.Export.Authentication.ExternalAuthenticationSection, PX.Export" />
      <section name="webDAV" type="PX.Export.WebDAV.WebDAVSection, PX.Export" />
      <section name="activeDirectory" type="PX.Data.Access.ActiveDirectorySection, PX.Data" />
      <section name="attachments" type="PX.Data.EP.DynamicAttachmentSection, PX.Data" />
      <section name="odata" type="PX.Api.OData.ODataConfigurationSection, PX.Api.OData" />
      <section name="fullTrustAssemblies" type="System.Web.Configuration.FullTrustAssembliesSection, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" allowDefinition="MachineToApplication" />
    </sectionGroup>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  </configSections>
  <connectionStrings>
    <remove name="ProjectX" />
    <add name="ProjectX" providerName="System.Data.SqlClient" connectionString="Data Source=DESKTOP-0EC1M8R;Initial Catalog=MohawkDB;Integrated Security=False;User ID=sa;Password=123" />
  </connectionStrings>
  <location inheritInChildApplications="false">
    <system.web>
      <machineKey validationKey="187C46E676C11D108F5AB1CEAB16A4CABFB1DEF8A77B54045FBAE8556E015744C6A5099963847BD36D3B673752455ABBC4528CF24A0F2EE3F1B3AC49397C0F51" decryptionKey="2134B0CA7E59C637F365F74FA10727B6C2BDC2E9D26ADE2A" validation="SHA1" />
      <trust level="Full" originUrl="" />
      <fullTrustAssemblies />
      <pxdatabase defaultProvider="PXSqlDatabaseProvider">
        <providers>
          <remove name="PXSqlDatabaseProvider" />
          <add name="PXSqlDatabaseProvider" type="PX.Data.PXSqlDatabaseProvider, PX.Data" connectionStringName="ProjectX" companyID="" secureCompanyID="False" />
        </providers>
      </pxdatabase>
      <pxaccess defaultProvider="PXDatabaseAccessProvider">
        <providers>
          <remove name="PXDatabaseAccessProvider" />
          <add name="PXDatabaseAccessProvider" type="PX.Data.PXDBFeatureAccessProvider, PX.Data" applicationName="/" administratorRole="Administrator" />
        </providers>
      </pxaccess>
      <membership defaultProvider="PXActiveDirectorySyncMembershipProvider">
        <providers>
          <remove name="PXActiveDirectorySyncMembershipProvider" />
          <remove name="MySQLMembershipProvider" />
          <add name="PXActiveDirectorySyncMembershipProvider" type="PX.Data.PXActiveDirectorySyncMembershipProvider, PX.Data" mainProviderType="PX.Data.PXDatabaseMembershipProvider" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false" passwordFormat="Clear" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="10" passwordStrengthRegularExpression="" />
        </providers>
      </membership>
      <siteMap enabled="true" defaultProvider="PXWizardSiteMapProvider">
        <providers>
          <remove name="PXWikiProvider" />
          <remove name="PXWizardSiteMapProvider" />
          <remove name="PXDatabaseSiteMapProvider" />
          <remove name="MySqlSiteMapProvider" />
          <!-- that can be planted into machine.config by mysql .net connector installation -->
          <remove name="PXOuterSiteMapProvider" />
          <add name="PXDatabaseSiteMapProvider" type="PX.Data.PXDatabaseSiteMapProvider, PX.Data" securityTrimmingEnabled="true" table="PX.SM.SiteMap" />
          <add name="PXWizardSiteMapProvider" type="PX.Objects.WZ.PXWizardSiteMapProvider, PX.Objects" securityTrimmingEnabled="true" />
          <add name="PXWikiProvider" type="PX.Data.PXWikiProvider, PX.Data" securityTrimmingEnabled="true" />
        </providers>
      </siteMap>
    </system.web>
  </location>
</configuration>

so many details in app.config are needed because with shims you can't modify everything in your graph. 

Next goes code, that allows you to create any graph in case if your app.config is ready:

public class GraphCreator<Twhere T : PXGraphnew()
{
    public static T CreateInstance()
    {
        string userName = "admin";
        using (var pxLoginScope = new PXLoginScope(userName, new string[0]))
        {
            var membershipUser = (MembershipUsernull;
 
            membershipUser = Membership.GetUser(pxLoginScope.UserName);
            if (membershipUser != null)
            {
                pxLoginScope.UserName = membershipUser.UserName;
                userName = PXContext.PXIdentity.User.Identity.Name;
                if (pxLoginScope.CompanyName != null)
                {
                    string[] rolesForUser = System.Web.Security.Roles.GetRolesForUser(pxLoginScope.UserName);
                    if (rolesForUser == null || rolesForUser.Length == 0)
                        throw new PXException("You are not allowed to login to the company {0}."new object[1]
                        {
                            (object) pxLoginScope.CompanyName
                        });
                    else if (
                        !Enumerable.Contains<string>((IEnumerable<string>) PXDatabase.AvailableCompanies,
                            pxLoginScope.CompanyName))
                        throw new PXException("You are not allowed to login to the company {0}."new object[1]
                        {
                            (object) pxLoginScope.CompanyName
                        });
                }
                PXLogin.SetBranchID(pxLoginScope.UserName, pxLoginScope.CompanyName, pxLoginScope.Branch);
               
            }
        }
        var gr = PXGraph.CreateInstance<T>();
        return gr;
    }
}

With such preparation you can try to shim and unit test your code. While I should warn you it will not be an easy job.

2 Comments

  • Jonathan said

    Question Yuriy, are you able to integrate your Unit test into a CI/CD tool like TeamCity or something of that nature with this approach?

  • docotor said

    Hi Jonathan, no I didn't try to integrate it with CI/CD. But I suppose it can be great mental exercise. Also I think this is achievable task

Add a Comment
Comments are closed