{"id":"MwT0RVg2jCM","title":"Lesson 3: Designing Daml Tests","description":"We'll look at how to construct modularized test scripts for Daml code. The video walks us through the basic syntax for writing test scripts and how to interpret and understand the script results. We also learn that test scripts should be written not only for the happy path but also for the unhappy path to ensure that our templates and choices are designed in such a way that only valid transactions are submitted to the ledger. We also see how to modularize our test scripts to make them more manageable and reusable for unit testing. This tutorial is a great resource for anyone looking to write test scripts for their Daml applications.\n\n00:39 Defining a test script\n01:41 Use of the left arrow\n02:40 Create and Exercise\n03:36 Return types of scrips\n05:32 Script Results\n07:09 Happy path and unhappy path\n08:34 SubmitMustFail\n10:22 Breaking tests into functions and modules\n13:55 Step through the code again\n14:33 Challenge\n15:06 Review\n\nFor a better learning experience, sign up and enroll in this free course at: https://daml.talentlms.com/plus/catalog/courses/144","channel_name":"Digital Asset Training","published_at":"2025-02-20T00:00:00.000Z","thumbnail_url":"https://i.ytimg.com/vi/MwT0RVg2jCM/hqdefault.jpg","transcript_text":"We're going to look at how to construct test scripts for our Daml code. Our goal is to learn to write test scripts in a modularized way. Let's take a look at this module, social network user. At the top, there's an import statement that says import Daml.Script. To write Daml scripts, we must import the Daml.Script module. After the import statement, there's the template social network user. This template takes two parameters, username, which is a party, and following, which is a list of parties that the user is following. Once a contract is created from the social network user template, we can exercise the choice follow on the contract to follow another party. Let's write our first test scripts to see if we can indeed create contracts and have users follow each other. We can write our script in the same file under where the template ends. There are two ways to define the scope of test scripts. First, we define the signature of our test by writing test, colon script, empty parenthesis. Then we can either one, open up a do-block or two, use the helper script for readability and then open a do-block. Either way works perfectly fine. We can also omit the script signature as Daml will notice the do-block and understand that we're constructing a script through type inference. For the purpose of our demo, we'll use both the signature and the helper. We won't use any arguments in the signature of our script, since our script doesn't require any data from outside of the script. Since do always starts a block, the rest of the script is indented. First, we want parties, Alice and Bob, to be known identities on our simulated ledger. We can use the function allocateParty to put a party called Alice in the variable alice. Notice that we're using the left arrow instead of the equal sign. The function allocatePary is an action that can only be performed once the script is run in the context of a ledger. Left arrow means run the action and bind the result. The submit function takes two arguments. A party, and the commands. Just like script is written in a do-block, commands are also written in a do-block. This command translates to a list of commands that will be submitted to the ledge. They'll create a transaction that creates a social network user contract with the value of the field, username set to the party bound to the variable Alice. The value of the field following is set to an empty list. You might be more familiar with passing arguments in the with block using multi lines, but we can also write the commands as a one-liner by separating each field and input with a semicolon. When there are more than one or two fields that require input arguments, you can write them in multi-lines for readability. Let's take a moment to look at how create and exercise are used in a template versus a script. The create command is analogous to the create statement that we use inside a choice body, similar to how exercise is used inside a choice body. When we exercise a choice on a contract, in our test script, we use exercise cmd. The version with the CMD suffix is always used on the client side to build up the list of commands on the ledger, while the ones without the suffix are used within choices and are executed directly on the server to be more specific, just like the create function used in choices. The Create CMD function results in the create action being executed on the ledger. However, create and create CMD have different signatures. The return type of the create function is update and the return type of create CMD is commands. Note, in a test script, commands gets passed as the second argument of the submit function, which returns script. Both the update and script types are instances of the action type. However, update represents an action executed within the Daml application running on the ledger, whereas script simulates the submit of commands from the Daml client application to the ledger and as a Daml client application is executed off ledger. In a similar way, the exercise function is used in choices. An exercise command is used in Daml script to exercise a choice on a contract. Going back to our script, we bind the result of Alice's submission to a variable aliceCid, as in Alice contract id. But notice we didn't bind the result of the second submit function call to any variable. Since we won't be exercising choices on Bob's social network user contract, we don't need to bind the result to a variable using the left arrow. Do note the last return statement is needed. Otherwise, the final return type won't match with our initial script signature, which will cause a type check error. Remember how we mentioned there are two ways to define the scope of a script? In this case, we could remove the script signature on the top. We don't need the last return statement, as Daml can infer the script signature based on the type of the final return value of the script. In this case, Daml comes up with a signature that is designed to return a contract id, social network user as a final return value. But do note we can't bind the result of our last submit statement to a variable and leave it unused. Daml will throw an error. So if you decide to omit return, you must one; make sure that the last statement in the script is an expression that returns a value and not a statement that binds the result of an expression to a variable. And two, make sure the return type in the script signature matches the actual return type, meaning the return type of the last expression. Now if we look at the script results, you see that two contracts of the test script have been created with their parameters in the right column of the table. The X indicates which party can view the contract as expected. Alice can see her social network user contract, but not Bob's and vice versa. Now let's take a look at the transaction view. This shows a full transaction graph of your script. Right now it consists of two social network user contracts. Clicking the link next to a transaction timestamp will take us to the exact place in the script where the transaction was created. Clicking on a template name will take us to the definition of the template in the source code. At the end of the transaction view, we see a list of active contracts. Clicking on them will take us to the location of their creation within the transaction. In the previous step, we created a social network user template for both parties, Alice and Bob in our test script. Now we can start exercising choices on them. As we saw earlier, we should use a function with a command suffix for writing our test. And since we're going to exercise a choice, we'll use exercise command to exercise the follow choice on aliceCid to have Alice follow Bob. We combine the result of the exercise to the variable newAliceCid. If we look here, the script results have cahnged. As you see, Alice's original social network user contract has been archived and replaced with a new one where she now follows Bob. Just a friendly reminder to see archived contracts. Simply click the show Archive checkbox in the script results pane. So far, we've tested the workflow we intend users of the application to follow, and it works as expected. The creation of a contract and the exercising of the choice succeeds when it's supposed to. This is referred to as the happy path. However, it's essential to also test that things fail when they're supposed to fail. Smart contracts in general run in the most adversarial environment possible, where every participant in the application could have access to the application source code and to parts of the infrastructure running the application. In other words, there's a chance a participant could act maliciously if they so choose. For this reason, a smart contract application must not only ensure that it implements the intended workflow, but also that no unintended workflow is possible within the application. In other words, we must test that no one can create a Daml client application that would send commands to the ledger that result in unintended consequences. In practice, this means testing actions that could be exploited for malicious purposes fail. This is referred to as testing the unhappy. The submit function we've used so far to send commands to the ledger aborts, the execution of the script when the submitted action fails. This isn't convenient for testing the unhappy path, as we may want to test several actions in the same script to facilitate negative testing. Daml script has a submitMustFail function, which is similar to submit, except that it fails and aborts the script execution when the submitted command succeeds and continues the script execution when the submitted command fail. For instance, a party should not be able to exercise choices on an archived contract. In this case, we can attempt to exercise the follow choice on aliceCid, which should have been archived. Using submitMustFail on a successful call, submitting transactions should fail. We can also test to make sure that a user can't follow themselves by attempting to have Alice follow herself using submitMustFail. Lastly, we can test to see if a user can follow another user twice by having Alice attempt to follow Bob for the second time using newAliceCid. By design of the application that we're creating, if any of these commands are submitted to the ledger, they're supposed to fail. The submitMustFail function allows us to test that they indeed fail. If a command submitted through submitMustFail succeeds, the Daml script will throw an error. The statements written with submitMustFail, don't affect our ledger, whether they succeed or fail, which is super convenient for us. As we see in our script table view. Alice follows Bob only once and she doesn't follow herself. The archived contract remains archived and unaffected. By clicking the show detailed disclosure, we can see the rules of different parties in each contract. For instance, in the last contract in the table, Alice is s as in signatory, and Bob is O as an. In the script transaction view for Transaction Zero all the way to transaction two, one, we can see the result of all of our Happy Path testing. And transaction 3, 4, 5 show the result of our Unhappy Path tests. So far we've been writing all of our test scripts in a single do-block. It worked fine for the purpose of our demo for one template, but what if we were to write the test scripts for a whole project with several parties and template? Writing all of our test scripts in a single do-block would make it increasingly difficult for us to manage our test scripts as our code grows. A good practice for writing a large amount of test scripts is to modularize them by unit to test each part of our template and choices individually. Now, let's take a look at how we could restructure our test script in a modularized way. Let's say we want to create two unit tests, one for the happy path on the exercise of the follow. The other for the unhappy path on following yourself. First, we need to allocate parties and create a social network user contract. This is the setup we need for both of our tests. If we were to write these tests linearly in each of the happy and unhappy path tests, we need to allocate the parties and create the contract. In other words, we'd have to write the same code twice, which isn't great. Instead, we're going to separate party allocation and contract creation into reusable functions that we can use in any test that requires a similar setup. We start by declaring a record with the keyword data and create a type test parties. We can define its constructor by specifying the fields. Since we're declaring the record to create parties, we can specify both Alice and Bob to be parties. Then in the setup party signature, we set our return type to test parties and open up a do-block and allocate parties using the two parties that we allocate. We instantiate the record test parties with Alice and Bob. Now that we have parties, we can create contracts unlike how we created parties using records. We can create contracts by writing a reusable function. This way we can call the function whenever we need to create contracts with different variable. The setup contracts function is designed to take a party as an argument and return contract id, social network user for testing. Next, we have a test for a happy path where Alice successfully exercises the follow choice to follow another party, bob. First we call setup parties to bind the result of the call to the variable test parties. Since test parties is an action that is carried out in the context of a ledger and not a pure expression, we use a left arrow to bind the result of set up parties to test parties. As a result, we get a record data with the type of test parties, which contains Alice and Bob as key value pairs to pass in Alice to set up contracts. As an argument, we must key into test parties to access Alice as in test parties dot Alice, which evaluates to the party Alice. The same goes for the submit statement and exercise command for the follow choice that takes Bob as an argument. We pass in test parties dot Bob instead of just Bob having to key into the record. Every time we need to access parties could lead to typos, cause syntax errors, and require repeating code and constantly having to write test parties. What we can do instead for the unhappy path, is to use the at sign, which is called the explicit type application. We can call set parties to extract the output, the parties for Alison and Bob, and pass them to the test parties constructor to create a record. This record gets bound to the variable test parties. Thanks to the type application, Alison and Bob, become available as local variables without us having to key into them like test parties dot Alice. Let's walk through the line of code once again to be extra. We define the variable test parties specifying its type as test party's record. The notation in the curly braces binds all fields within the test party's record to namesake local variables. In other words, we create a local variable named Alice and bind to the field named Alice within the test party's record and so on for the other fields to reduce code repetition. You can replace Allison and Bob in the curly braces with two dots. This refers to all fields extracted from setup parties. Now the best thing you can do is practice these test methods. Try reconstructing the test script for the happy path, using the ad sign, and also modularize the other two tests we wrote for the unhappy path test. Building a habit of writing modularized test scripts is crucial for creating a production level Applic. Not only shows your understanding of the Daml language, but it also allows you to test the functionality of your code separately, unit by unit as you collaborate with your team members. You'll also realize how writing modularized test scripts brings clarity to your teamwork. So let's review what we covered. We took a look at the basic syntax for writing test scripts, as well as how we can interpret and understand the script results. Test scripts should be written not only for the happy path, but also for the unhappy. Typically, there are many more unhappy scenarios than happy paths that need to be tested. So the bulk of the work in writing test scripts for a Daml application is creating scripts for all the possible unhappy paths. This ensures that our templates and choices are designed in such a way the parties are only able to submit valid transactions to the ledger. Finally, we restructured our test scripts to modularize it, to make it more manageable and reusable for unit.","transcript_synced_at":"2026-10-26T03:58:06.560Z","segments":[{"start_seconds":0,"text":"We're going to look at how to construct test scripts for our Daml code. Our goal is to learn to write test scripts in a modularized way.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=0s"},{"start_seconds":8,"text":"Let's take a look at this module, social network user. At the top, there's an import statement that says import Daml.Script.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=8s"},{"start_seconds":15,"text":"To write Daml scripts, we must import the Daml.Script module. After the import statement, there's the template social network user.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=15s"},{"start_seconds":23,"text":"This template takes two parameters, username, which is a party, and following, which is a list of parties that the user is following.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=23s"},{"start_seconds":31,"text":"Once a contract is created from the social network user template, we can exercise the choice follow on the contract to follow another party.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=31s"},{"start_seconds":39,"text":"Let's write our first test scripts to see if we can indeed create contracts and have users follow each other. We can write our script in the same file under where the template ends.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=39s"},{"start_seconds":48,"text":"There are two ways to define the scope of test scripts.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=48s"},{"start_seconds":51,"text":"First, we define the signature of our test by writing test, colon script, empty parenthesis.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=51s"},{"start_seconds":57,"text":"Then we can either one, open up a do-block or two, use the helper script for readability and then open a do-block.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=57s"},{"start_seconds":65,"text":"Either way works perfectly fine.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=65s"},{"start_seconds":67,"text":"We can also omit the script signature as Daml will notice the do-block and understand that we're constructing a script through type inference.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=67s"},{"start_seconds":75,"text":"For the purpose of our demo, we'll use both the signature and the helper.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=75s"},{"start_seconds":79,"text":"We won't use any arguments in the signature of our script, since our script doesn't require any data from outside of the script.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=79s"},{"start_seconds":85,"text":"Since do always starts a block, the rest of the script is indented.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=85s"},{"start_seconds":90,"text":"First, we want parties, Alice and Bob, to be known identities on our simulated ledger.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=90s"},{"start_seconds":96,"text":"We can use the function allocateParty to put a party called Alice in the variable alice. Notice that we're using the left arrow instead of the equal sign.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=96s"},{"start_seconds":104,"text":"The function allocatePary is an action that can only be performed once the script is run in the context of a ledger.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=104s"},{"start_seconds":112,"text":"Left arrow means run the action and bind the result. The submit function takes two arguments. A party, and the commands.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=112s"},{"start_seconds":120,"text":"Just like script is written in a do-block, commands are also written in a do-block.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=120s"},{"start_seconds":125,"text":"This command translates to a list of commands that will be submitted to the ledge.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=125s"},{"start_seconds":129,"text":"They'll create a transaction that creates a social network user contract with the value of the field, username set to the party bound to the variable Alice.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=129s"},{"start_seconds":138,"text":"The value of the field following is set to an empty list.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=138s"},{"start_seconds":142,"text":"You might be more familiar with passing arguments in the with block using multi lines, but we can also write the commands as a one-liner by separating each field and input with a semicolon.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=142s"},{"start_seconds":152,"text":"When there are more than one or two fields that require input arguments, you can write them in multi-lines for readability.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=152s"},{"start_seconds":160,"text":"Let's take a moment to look at how create and exercise are used in a template versus a script.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=160s"},{"start_seconds":166,"text":"The create command is analogous to the create statement that we use inside a choice body, similar to how exercise is used inside a choice body.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=166s"},{"start_seconds":174,"text":"When we exercise a choice on a contract, in our test script, we use exercise cmd.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=174s"},{"start_seconds":178,"text":"The version with the CMD suffix is always used on the client side to build up the list of commands on the ledger, while the ones without the suffix are used","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=178s"},{"start_seconds":187,"text":"within choices and are executed directly on the server to be more specific, just like the create function used in choices.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=187s"},{"start_seconds":194,"text":"The Create CMD function results in the create action being executed on the ledger. However, create and create CMD have different signatures.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=194s"},{"start_seconds":203,"text":"The return type of the create function is update and the return type of create CMD is commands.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=203s"},{"start_seconds":210,"text":"Note, in a test script, commands gets passed as the second argument of the submit function, which returns script. Both the update and script types are instances of the action type.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=210s"},{"start_seconds":222,"text":"However, update represents an action executed within the Daml application running on the ledger, whereas script simulates the submit of commands","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=222s"},{"start_seconds":231,"text":"from the Daml client application to the ledger and as a Daml client application is executed off ledger.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=231s"},{"start_seconds":238,"text":"In a similar way, the exercise function is used in choices. An exercise command is used in Daml script to exercise a choice on a contract.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=238s"},{"start_seconds":248,"text":"Going back to our script, we bind the result of Alice's submission to a variable aliceCid, as in Alice contract id.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=248s"},{"start_seconds":257,"text":"But notice we didn't bind the result of the second submit function call to any variable.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=257s"},{"start_seconds":262,"text":"Since we won't be exercising choices on Bob's social network user contract, we don't need to bind the result to a variable using the left arrow.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=262s"},{"start_seconds":272,"text":"Do note the last return statement is needed.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=272s"},{"start_seconds":275,"text":"Otherwise, the final return type won't match with our initial script signature, which will cause a type check error. Remember how we mentioned there are two ways to define the scope of a script?","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=275s"},{"start_seconds":284,"text":"In this case, we could remove the script signature on the top.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=284s"},{"start_seconds":288,"text":"We don't need the last return statement, as Daml can infer the script signature based on the type of the final return value of the script.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=288s"},{"start_seconds":296,"text":"In this case, Daml comes up with a signature that is designed to return a contract id, social network user as a final return value.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=296s"},{"start_seconds":305,"text":"But do note we can't bind the result of our last submit statement to a variable and leave it unused. Daml will throw an error.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=305s"},{"start_seconds":313,"text":"So if you decide to omit return, you must one; make sure that the last statement in the script is an expression that returns a value and not a statement that binds the result of an expression to a variable.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=313s"},{"start_seconds":323,"text":"And two, make sure the return type in the script signature matches the actual return type, meaning the return type of the last expression.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=323s"},{"start_seconds":332,"text":"Now if we look at the script results, you see that two contracts of the test script have been created with their parameters in the right column of the table.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=332s"},{"start_seconds":341,"text":"The X indicates which party can view the contract as expected. Alice can see her social network user contract, but not Bob's and vice versa.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=341s"},{"start_seconds":350,"text":"Now let's take a look at the transaction view. This shows a full transaction graph of your script. Right now it consists of two social network user contracts.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=350s"},{"start_seconds":360,"text":"Clicking the link next to a transaction timestamp will take us to the exact place in the script where the transaction was created.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=360s"},{"start_seconds":367,"text":"Clicking on a template name will take us to the definition of the template in the source code. At the end of the transaction view, we see a list of active contracts.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=367s"},{"start_seconds":377,"text":"Clicking on them will take us to the location of their creation within the transaction.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=377s"},{"start_seconds":381,"text":"In the previous step, we created a social network user template for both parties, Alice and Bob in our test script. Now we can start exercising choices on them.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=381s"},{"start_seconds":391,"text":"As we saw earlier, we should use a function with a command suffix for writing our test.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=391s"},{"start_seconds":396,"text":"And since we're going to exercise a choice, we'll use exercise command to exercise the follow choice on aliceCid to have Alice follow Bob.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=396s"},{"start_seconds":405,"text":"We combine the result of the exercise to the variable newAliceCid. If we look here, the script results have cahnged.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=405s"},{"start_seconds":414,"text":"As you see, Alice's original social network user contract has been archived and replaced with a new one where she now follows Bob.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=414s"},{"start_seconds":422,"text":"Just a friendly reminder to see archived contracts. Simply click the show Archive checkbox in the script results pane.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=422s"},{"start_seconds":429,"text":"So far, we've tested the workflow we intend users of the application to follow, and it works as expected.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=429s"},{"start_seconds":435,"text":"The creation of a contract and the exercising of the choice succeeds when it's supposed to. This is referred to as the happy path.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=435s"},{"start_seconds":444,"text":"However, it's essential to also test that things fail when they're supposed to fail.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=444s"},{"start_seconds":449,"text":"Smart contracts in general run in the most adversarial environment possible, where every participant in the application could have access to the application source code and to parts of the infrastructure running the application.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=449s"},{"start_seconds":463,"text":"In other words, there's a chance a participant could act maliciously if they so choose.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=463s"},{"start_seconds":468,"text":"For this reason, a smart contract application must not only ensure that it implements the intended workflow, but also that no unintended workflow is possible within the application.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=468s"},{"start_seconds":479,"text":"In other words, we must test that no one can create a Daml client application that would send commands to the ledger that result in unintended consequences.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=479s"},{"start_seconds":488,"text":"In practice, this means testing actions that could be exploited for malicious purposes fail. This is referred to as testing the unhappy.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=488s"},{"start_seconds":497,"text":"The submit function we've used so far to send commands to the ledger aborts, the execution of the script when the submitted action fails.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=497s"},{"start_seconds":505,"text":"This isn't convenient for testing the unhappy path, as we may want to test several actions in the same script to facilitate negative testing.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=505s"},{"start_seconds":514,"text":"Daml script has a submitMustFail function, which is similar to submit, except that it fails and aborts the script execution when the submitted command succeeds and continues the script execution when the submitted command fail.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=514s"},{"start_seconds":528,"text":"For instance, a party should not be able to exercise choices on an archived contract.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=528s"},{"start_seconds":534,"text":"In this case, we can attempt to exercise the follow choice on aliceCid, which should have been archived.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=534s"},{"start_seconds":540,"text":"Using submitMustFail on a successful call, submitting transactions should fail.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=540s"},{"start_seconds":547,"text":"We can also test to make sure that a user can't follow themselves by attempting to have Alice follow herself using submitMustFail.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=547s"},{"start_seconds":555,"text":"Lastly, we can test to see if a user can follow another user twice by having Alice attempt to follow Bob for the second time using newAliceCid.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=555s"},{"start_seconds":564,"text":"By design of the application that we're creating, if any of these commands are submitted to the ledger, they're supposed to fail. The submitMustFail function allows us to test that they indeed fail.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=564s"},{"start_seconds":574,"text":"If a command submitted through submitMustFail succeeds, the Daml script will throw an error.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=574s"},{"start_seconds":579,"text":"The statements written with submitMustFail, don't affect our ledger, whether they succeed or fail, which is super convenient for us. As we see in our script table view.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=579s"},{"start_seconds":588,"text":"Alice follows Bob only once and she doesn't follow herself. The archived contract remains archived and unaffected.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=588s"},{"start_seconds":595,"text":"By clicking the show detailed disclosure, we can see the rules of different parties in each contract.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=595s"},{"start_seconds":601,"text":"For instance, in the last contract in the table, Alice is s as in signatory, and Bob is O as an.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=601s"},{"start_seconds":609,"text":"In the script transaction view for Transaction Zero all the way to transaction two, one, we can see the result of all of our Happy Path testing.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=609s"},{"start_seconds":617,"text":"And transaction 3, 4, 5 show the result of our Unhappy Path tests. So far we've been writing all of our test scripts in a single do-block.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=617s"},{"start_seconds":626,"text":"It worked fine for the purpose of our demo for one template, but what if we were to write the test scripts for a whole project with several parties and template?","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=626s"},{"start_seconds":635,"text":"Writing all of our test scripts in a single do-block would make it increasingly difficult for us to manage our test scripts as our code grows.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=635s"},{"start_seconds":642,"text":"A good practice for writing a large amount of test scripts is to modularize them by unit to test each part of our template and choices individually.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=642s"},{"start_seconds":651,"text":"Now, let's take a look at how we could restructure our test script in a modularized way.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=651s"},{"start_seconds":656,"text":"Let's say we want to create two unit tests, one for the happy path on the exercise of the follow. The other for the unhappy path on following yourself.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=656s"},{"start_seconds":665,"text":"First, we need to allocate parties and create a social network user contract. This is the setup we need for both of our tests.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=665s"},{"start_seconds":673,"text":"If we were to write these tests linearly in each of the happy and unhappy path tests, we need to allocate the parties and create the contract.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=673s"},{"start_seconds":681,"text":"In other words, we'd have to write the same code twice, which isn't great.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=681s"},{"start_seconds":685,"text":"Instead, we're going to separate party allocation and contract creation into reusable functions that we can use in any test that requires a similar setup.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=685s"},{"start_seconds":695,"text":"We start by declaring a record with the keyword data and create a type test parties. We can define its constructor by specifying the fields.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=695s"},{"start_seconds":704,"text":"Since we're declaring the record to create parties, we can specify both Alice and Bob to be parties.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=704s"},{"start_seconds":710,"text":"Then in the setup party signature, we set our return type to test parties and open up a do-block and allocate parties using the two parties that we allocate.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=710s"},{"start_seconds":719,"text":"We instantiate the record test parties with Alice and Bob.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=719s"},{"start_seconds":723,"text":"Now that we have parties, we can create contracts unlike how we created parties using records. We can create contracts by writing a reusable function.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=723s"},{"start_seconds":733,"text":"This way we can call the function whenever we need to create contracts with different variable.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=733s"},{"start_seconds":739,"text":"The setup contracts function is designed to take a party as an argument and return contract id, social network user for testing.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=739s"},{"start_seconds":747,"text":"Next, we have a test for a happy path where Alice successfully exercises the follow choice to follow another party, bob.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=747s"},{"start_seconds":754,"text":"First we call setup parties to bind the result of the call to the variable test parties.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=754s"},{"start_seconds":760,"text":"Since test parties is an action that is carried out in the context of a ledger and not a pure expression, we use a left arrow to bind the result of set up parties to test parties.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=760s"},{"start_seconds":770,"text":"As a result, we get a record data with the type of test parties, which contains Alice and Bob as key value pairs to pass in Alice to set up contracts.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=770s"},{"start_seconds":779,"text":"As an argument, we must key into test parties to access Alice as in test parties dot Alice, which evaluates to the party Alice.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=779s"},{"start_seconds":788,"text":"The same goes for the submit statement and exercise command for the follow choice that takes Bob as an argument. We pass in test parties dot Bob instead of just Bob having to key into the record.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=788s"},{"start_seconds":799,"text":"Every time we need to access parties could lead to typos, cause syntax errors, and require repeating code and constantly having to write test parties.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=799s"},{"start_seconds":808,"text":"What we can do instead for the unhappy path, is to use the at sign, which is called the explicit type application.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=808s"},{"start_seconds":815,"text":"We can call set parties to extract the output, the parties for Alison and Bob, and pass them to the test parties constructor to create a record.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=815s"},{"start_seconds":823,"text":"This record gets bound to the variable test parties.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=823s"},{"start_seconds":827,"text":"Thanks to the type application, Alison and Bob, become available as local variables without us having to key into them like test parties dot Alice.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=827s"},{"start_seconds":835,"text":"Let's walk through the line of code once again to be extra.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=835s"},{"start_seconds":840,"text":"We define the variable test parties specifying its type as test party's record.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=840s"},{"start_seconds":846,"text":"The notation in the curly braces binds all fields within the test party's record to namesake local variables.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=846s"},{"start_seconds":852,"text":"In other words, we create a local variable named Alice and bind to the field named Alice within the test party's record and so on for the other fields to reduce code repetition.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=852s"},{"start_seconds":864,"text":"You can replace Allison and Bob in the curly braces with two dots. This refers to all fields extracted from setup parties.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=864s"},{"start_seconds":873,"text":"Now the best thing you can do is practice these test methods.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=873s"},{"start_seconds":876,"text":"Try reconstructing the test script for the happy path, using the ad sign, and also modularize the other two tests we wrote for the unhappy path test.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=876s"},{"start_seconds":885,"text":"Building a habit of writing modularized test scripts is crucial for creating a production level Applic.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=885s"},{"start_seconds":891,"text":"Not only shows your understanding of the Daml language, but it also allows you to test the functionality of your code separately, unit by unit as you collaborate with your team members.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=891s"},{"start_seconds":901,"text":"You'll also realize how writing modularized test scripts brings clarity to your teamwork. So let's review what we covered.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=901s"},{"start_seconds":908,"text":"We took a look at the basic syntax for writing test scripts, as well as how we can interpret and understand the script results.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=908s"},{"start_seconds":916,"text":"Test scripts should be written not only for the happy path, but also for the unhappy.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=916s"},{"start_seconds":921,"text":"Typically, there are many more unhappy scenarios than happy paths that need to be tested.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=921s"},{"start_seconds":926,"text":"So the bulk of the work in writing test scripts for a Daml application is creating scripts for all the possible unhappy paths.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=926s"},{"start_seconds":934,"text":"This ensures that our templates and choices are designed in such a way the parties are only able to submit valid transactions to the ledger.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=934s"},{"start_seconds":941,"text":"Finally, we restructured our test scripts to modularize it, to make it more manageable and reusable for unit.","url":"https://www.youtube.com/watch?v=MwT0RVg2jCM&t=941s"}],"youtube_url":"https://youtube.com/watch?v=MwT0RVg2jCM","url":"https://ccpedia.xyz/videos/MwT0RVg2jCM"}